Saya dihadapkan dengan masalah ini sekitar setahun yang lalu ketika datang untuk mencari informasi yang dimasukkan pengguna tentang rig minyak dalam database informasi lain-lain. Tujuannya adalah untuk melakukan semacam pencarian string fuzzy yang dapat mengidentifikasi entri database dengan elemen yang paling umum.
Bagian dari penelitian ini melibatkan penerapan algoritma jarak Levenshtein , yang menentukan berapa banyak perubahan yang harus dilakukan pada string atau frasa untuk mengubahnya menjadi string atau frasa lain.
Implementasi yang saya buat relatif sederhana, dan melibatkan perbandingan panjang dari dua frasa, jumlah perubahan antara setiap frasa, dan apakah setiap kata dapat ditemukan dalam entri target.
Artikel ini ada di situs pribadi jadi saya akan melakukan yang terbaik untuk menambahkan konten yang relevan di sini:
Fuzzy String Matching adalah proses melakukan estimasi mirip-manusia dari kesamaan dua kata atau frasa. Dalam banyak kasus, ini melibatkan mengidentifikasi kata atau frasa yang paling mirip satu sama lain. Artikel ini menjelaskan solusi internal untuk masalah pencocokan string fuzzy dan kegunaannya dalam memecahkan berbagai masalah yang memungkinkan kita untuk mengotomatisasi tugas-tugas yang sebelumnya membutuhkan keterlibatan pengguna yang membosankan.
pengantar
Kebutuhan untuk melakukan pencocokan string fuzzy awalnya muncul saat mengembangkan alat Validator Teluk Meksiko. Apa yang ada adalah basis data jurang rig dan platform minyak Meksiko yang dikenal, dan orang-orang yang membeli asuransi akan memberi kami beberapa informasi yang diketik dengan buruk tentang aset mereka dan kami harus mencocokkannya dengan database platform yang dikenal. Ketika informasi yang diberikan sangat sedikit, yang terbaik yang bisa kami lakukan adalah mengandalkan penjamin emisi untuk "mengenali" yang mereka maksudkan dan memanggil informasi yang tepat. Di sinilah solusi otomatis ini berguna.
Saya menghabiskan satu hari meneliti metode pencocokan string fuzzy, dan akhirnya menemukan algoritma jarak Levenshtein yang sangat berguna di Wikipedia.
Penerapan
Setelah membaca tentang teori di baliknya, saya menerapkan dan menemukan cara untuk mengoptimalkannya. Ini adalah bagaimana kode saya terlihat di VBA:
'Calculate the Levenshtein Distance between two strings (the number of insertions,
'deletions, and substitutions needed to transform the first string into the second)
Public Function LevenshteinDistance(ByRef S1 As String, ByVal S2 As String) As Long
Dim L1 As Long, L2 As Long, D() As Long 'Length of input strings and distance matrix
Dim i As Long, j As Long, cost As Long 'loop counters and cost of substitution for current letter
Dim cI As Long, cD As Long, cS As Long 'cost of next Insertion, Deletion and Substitution
L1 = Len(S1): L2 = Len(S2)
ReDim D(0 To L1, 0 To L2)
For i = 0 To L1: D(i, 0) = i: Next i
For j = 0 To L2: D(0, j) = j: Next j
For j = 1 To L2
For i = 1 To L1
cost = Abs(StrComp(Mid$(S1, i, 1), Mid$(S2, j, 1), vbTextCompare))
cI = D(i - 1, j) + 1
cD = D(i, j - 1) + 1
cS = D(i - 1, j - 1) + cost
If cI <= cD Then 'Insertion or Substitution
If cI <= cS Then D(i, j) = cI Else D(i, j) = cS
Else 'Deletion or Substitution
If cD <= cS Then D(i, j) = cD Else D(i, j) = cS
End If
Next i
Next j
LevenshteinDistance = D(L1, L2)
End Function
Sederhana, cepat, dan metrik yang sangat berguna. Dengan menggunakan ini, saya membuat dua metrik terpisah untuk mengevaluasi kesamaan dua string. Yang saya sebut "valuePhrase" dan yang saya sebut "valueWords". valuePhrase hanyalah jarak Levenshtein antara dua frasa, dan valueWords membagi string menjadi kata-kata individual, berdasarkan pembatas seperti spasi, tanda hubung, dan apa pun yang Anda suka, dan membandingkan setiap kata dengan kata lain, meringkas setiap kata dengan kata lain, merangkum yang terpendek Jarak Levenshtein menghubungkan dua kata. Pada dasarnya, ini mengukur apakah informasi dalam satu 'frase' benar-benar terkandung dalam yang lain, sama seperti permutasi kata-bijaksana. Saya menghabiskan beberapa hari sebagai proyek sampingan dengan cara paling efisien untuk memisahkan string berdasarkan pembatas.
valueWords, valuePhrase, dan fungsi Split:
Public Function valuePhrase#(ByRef S1$, ByRef S2$)
valuePhrase = LevenshteinDistance(S1, S2)
End Function
Public Function valueWords#(ByRef S1$, ByRef S2$)
Dim wordsS1$(), wordsS2$()
wordsS1 = SplitMultiDelims(S1, " _-")
wordsS2 = SplitMultiDelims(S2, " _-")
Dim word1%, word2%, thisD#, wordbest#
Dim wordsTotal#
For word1 = LBound(wordsS1) To UBound(wordsS1)
wordbest = Len(S2)
For word2 = LBound(wordsS2) To UBound(wordsS2)
thisD = LevenshteinDistance(wordsS1(word1), wordsS2(word2))
If thisD < wordbest Then wordbest = thisD
If thisD = 0 Then GoTo foundbest
Next word2
foundbest:
wordsTotal = wordsTotal + wordbest
Next word1
valueWords = wordsTotal
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' SplitMultiDelims
' This function splits Text into an array of substrings, each substring
' delimited by any character in DelimChars. Only a single character
' may be a delimiter between two substrings, but DelimChars may
' contain any number of delimiter characters. It returns a single element
' array containing all of text if DelimChars is empty, or a 1 or greater
' element array if the Text is successfully split into substrings.
' If IgnoreConsecutiveDelimiters is true, empty array elements will not occur.
' If Limit greater than 0, the function will only split Text into 'Limit'
' array elements or less. The last element will contain the rest of Text.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function SplitMultiDelims(ByRef Text As String, ByRef DelimChars As String, _
Optional ByVal IgnoreConsecutiveDelimiters As Boolean = False, _
Optional ByVal Limit As Long = -1) As String()
Dim ElemStart As Long, N As Long, M As Long, Elements As Long
Dim lDelims As Long, lText As Long
Dim Arr() As String
lText = Len(Text)
lDelims = Len(DelimChars)
If lDelims = 0 Or lText = 0 Or Limit = 1 Then
ReDim Arr(0 To 0)
Arr(0) = Text
SplitMultiDelims = Arr
Exit Function
End If
ReDim Arr(0 To IIf(Limit = -1, lText - 1, Limit))
Elements = 0: ElemStart = 1
For N = 1 To lText
If InStr(DelimChars, Mid(Text, N, 1)) Then
Arr(Elements) = Mid(Text, ElemStart, N - ElemStart)
If IgnoreConsecutiveDelimiters Then
If Len(Arr(Elements)) > 0 Then Elements = Elements + 1
Else
Elements = Elements + 1
End If
ElemStart = N + 1
If Elements + 1 = Limit Then Exit For
End If
Next N
'Get the last token terminated by the end of the string into the array
If ElemStart <= lText Then Arr(Elements) = Mid(Text, ElemStart)
'Since the end of string counts as the terminating delimiter, if the last character
'was also a delimiter, we treat the two as consecutive, and so ignore the last elemnent
If IgnoreConsecutiveDelimiters Then If Len(Arr(Elements)) = 0 Then Elements = Elements - 1
ReDim Preserve Arr(0 To Elements) 'Chop off unused array elements
SplitMultiDelims = Arr
End Function
Ukuran Kesamaan
Dengan menggunakan dua metrik ini, dan yang ketiga yang hanya menghitung jarak antara dua string, saya memiliki serangkaian variabel yang dapat saya jalankan algoritma optimasi untuk mencapai jumlah kecocokan terbanyak. Pencocokan string fuzzy, itu sendiri, adalah ilmu fuzzy, dan dengan menciptakan metrik independen linier untuk mengukur kesamaan string, dan memiliki serangkaian string yang ingin kita cocokkan satu sama lain, kita dapat menemukan parameter itu, untuk gaya spesifik kita dari string, berikan hasil pertandingan fuzzy terbaik.
Pada awalnya, tujuan metrik adalah untuk memiliki nilai pencarian yang rendah untuk kecocokan yang tepat, dan meningkatkan nilai pencarian untuk tindakan yang semakin diijinkan. Dalam kasus yang tidak praktis, ini cukup mudah untuk didefinisikan menggunakan satu set permutasi yang terdefinisi dengan baik, dan merekayasa rumus akhir sedemikian sehingga mereka telah meningkatkan hasil nilai pencarian yang diinginkan.
Pada tangkapan layar di atas, saya mengubah heuristik saya untuk menghasilkan sesuatu yang saya rasa diskalakan dengan baik untuk perbedaan persepsi saya antara istilah pencarian dan hasil. Heuristik yang saya gunakan Value Phrase
dalam spreadsheet di atas adalah =valuePhrase(A2,B2)-0.8*ABS(LEN(B2)-LEN(A2))
. Saya secara efektif mengurangi penalti jarak Levenstein sebesar 80% dari perbedaan panjang dua "frasa". Dengan cara ini, "frasa" yang memiliki panjang yang sama menderita penalti penuh, tetapi "frasa" yang berisi 'informasi tambahan' (lebih lama) tetapi selain itu sebagian besar masih berbagi karakter yang sama menderita penalti berkurang. Saya menggunakan Value Words
fungsi apa adanya, dan kemudian tugas akhir sayaSearchVal
heuristik didefinisikan sebagai=MIN(D2,E2)*0.8+MAX(D2,E2)*0.2
- rata-rata tertimbang. Yang mana dari dua skor yang lebih rendah mendapat bobot 80%, dan 20% dari skor yang lebih tinggi. Ini hanya heuristik yang cocok dengan kasus penggunaan saya untuk mendapatkan tingkat kecocokan yang baik. Bobot ini adalah sesuatu yang kemudian dapat diubah untuk mendapatkan tingkat kecocokan terbaik dengan data pengujian mereka.
Seperti yang Anda lihat, dua metrik terakhir, yaitu metrik pencocokan string fuzzy, sudah memiliki kecenderungan alami untuk memberikan skor rendah ke string yang dimaksudkan untuk mencocokkan (diagonal). Ini sangat bagus.
Aplikasi
Untuk memungkinkan optimalisasi pencocokan fuzzy, saya menimbang setiap metrik. Dengan demikian, setiap aplikasi pencocokan string fuzzy dapat mempertimbangkan parameter secara berbeda. Formula yang mendefinisikan skor akhir adalah kombinasi sederhana dari metrik dan bobotnya:
value = Min(phraseWeight*phraseValue, wordsWeight*wordsValue)*minWeight
+ Max(phraseWeight*phraseValue, wordsWeight*wordsValue)*maxWeight
+ lengthWeight*lengthValue
Menggunakan algoritme pengoptimalan (jaringan saraf terbaik di sini karena merupakan masalah diskrit, multi-dimensi), tujuannya sekarang adalah untuk memaksimalkan jumlah kecocokan. Saya membuat fungsi yang mendeteksi jumlah kecocokan yang tepat dari setiap set satu sama lain, seperti yang dapat dilihat pada tangkapan layar akhir ini. Kolom atau baris mendapat poin jika skor terendah diberikan string yang dimaksudkan untuk dicocokkan, dan poin parsial diberikan jika ada dasi untuk skor terendah, dan pertandingan yang benar adalah di antara string yang cocok yang diikat. Saya kemudian mengoptimalkannya. Anda dapat melihat bahwa sel hijau adalah kolom yang paling cocok dengan baris saat ini, dan kotak biru di sekitar sel adalah baris yang paling cocok dengan kolom saat ini. Skor di sudut bawah kira-kira adalah jumlah pertandingan yang berhasil dan inilah yang kami katakan untuk memaksimalkan masalah optimasi kami.
Algoritma itu sukses luar biasa, dan parameter solusi mengatakan banyak tentang jenis masalah ini. Anda akan melihat skor yang dioptimalkan adalah 44, dan skor terbaik yang mungkin adalah 48. 5 kolom pada akhirnya adalah umpan, dan tidak memiliki kecocokan sama sekali dengan nilai baris. Semakin banyak umpan, semakin sulit untuk menemukan pasangan yang cocok.
Dalam kasus pencocokan khusus ini, panjang string tidak relevan, karena kami mengharapkan singkatan yang mewakili kata-kata yang lebih panjang, sehingga bobot optimal untuk panjang adalah -0,3, yang berarti kami tidak menghukum string yang panjangnya bervariasi. Kami mengurangi skor untuk mengantisipasi singkatan ini, memberikan lebih banyak ruang untuk kecocokan kata parsial untuk menggantikan kecocokan non-kata yang hanya membutuhkan lebih sedikit pergantian karena string lebih pendek.
Berat kata adalah 1,0 sedangkan bobot frasa hanya 0,5, yang berarti bahwa kita menghukum seluruh kata yang hilang dari satu string dan lebih menghargai keseluruhan frase menjadi utuh. Ini berguna karena banyak string ini memiliki satu kata yang sama (bahaya) di mana yang paling penting adalah apakah kombinasi (wilayah dan bahaya) dipertahankan atau tidak.
Akhirnya, bobot minimum dioptimalkan pada 10 dan bobot maksimum pada 1. Apa artinya ini adalah bahwa jika yang terbaik dari dua skor (frasa nilai dan kata-kata nilai) tidak terlalu baik, pertandingan akan dikenakan penalti, tetapi kami tidak akan sangat menghukum yang terburuk dari dua skor. Pada dasarnya, ini menempatkan penekanan pada yang membutuhkan baik dalam valueWord atau valuePhrase memiliki skor yang baik, tapi tidak keduanya. Semacam mentalitas "ambil apa yang bisa kita dapatkan".
Benar-benar menarik apa yang dioptimalkan nilai 5 bobot ini katakan tentang jenis pencocokan string fuzzy yang terjadi. Untuk kasus praktis yang sama sekali berbeda dari pencocokan string fuzzy, parameter ini sangat berbeda. Saya telah menggunakannya untuk 3 aplikasi terpisah sejauh ini.
Meskipun tidak digunakan dalam optimisasi akhir, lembar tolok ukur dibuat yang cocok dengan kolom untuk diri mereka sendiri untuk semua hasil sempurna di diagonal, dan memungkinkan pengguna mengubah parameter untuk mengontrol tingkat di mana skor berbeda dari 0, dan perhatikan kesamaan bawaan antara frase pencarian ( yang secara teori dapat digunakan untuk mengimbangi hasil positif palsu)
Aplikasi Lebih Lanjut
Solusi ini memiliki potensi untuk digunakan di mana saja di mana pengguna ingin memiliki sistem komputer mengidentifikasi string dalam serangkaian string di mana tidak ada kecocokan yang sempurna. (Seperti perkiraan vlookup pertandingan untuk string).
Jadi apa yang harus Anda ambil dari ini, adalah bahwa Anda mungkin ingin menggunakan kombinasi heuristik tingkat tinggi (menemukan kata-kata dari satu frasa di frasa lain, panjang kedua frasa, dll) bersamaan dengan penerapan algoritma jarak Levenshtein. Karena memutuskan yang mana yang "paling cocok" adalah penentuan heuristik (kabur) - Anda harus membuat satu set bobot untuk metrik yang Anda buat untuk menentukan kesamaan.
Dengan set heuristik dan bobot yang sesuai, Anda akan memiliki program perbandingan dengan cepat membuat keputusan yang akan Anda buat.