Существует множество бизнес-кейсов для сопоставления строк. Когда для точек данных не существует уникального идентификатора, существуют некоторые неструктурированные данные, чтобы сделать возможным сопоставление. В нашем случае неструктурированные данные - это текст. Например, некоторые могут соответствовать клиентам, продуктам, данные компании поступают из разных источников данных. Кроме того, банк может сопоставить информацию о клиентах с их аккаунтами в социальных сетях.

Мои работы в основном посвящены бизнес-проблемам и решениям, поэтому сопоставление строк будет использоваться в названиях продуктов на страницах продуктов Getir и CarrefourSA.

Шаги:

  • Описание и загрузка набора данных.
  • Измерение схожести строк (расстояние Левенштейна и отсортированное расстояние Левенштейна)
  • Проблемы с мерами сопоставления строк
  • Разработка функций в наборе данных
  • Прогнозирование совпадений с машинным обучением (персептрон, логистическая регрессия, машины опорных векторов, многослойный персептрон)

Набор данных

Набор данных представляет собой файл Excel, состоящий из столбцов externalName, FetchName, externalPrice, price, priceRate и Match. GetirName имеет названия продуктов в приложении Getir. Столбцы externalName и externalPrice предназначены для наименования и цены продуктов при поиске значений GetirName на веб-сайте CarrefourSA. Столбец Price предназначен для цен на продукты Getir, а priceRate равно price / externalPrice. Соответствие предназначено для целевой переменной, которая равна 1, если два имени совпадают, и 0, когда они не совпадают.

df= pd.read_excel('getir.xlsx', sheet_name='getir')
df.head(25)

Сходство измерительной строки

Для измерения сходства с парой строк построено несколько алгоритмов. Я использую Edit Distance, которое также известно как Levensthein Distance.

Минимальное расстояние редактирования между двумя строками - это минимальное количество операций редактирования, необходимых для преобразования одной строки в другую. Операции редактирования могут состоять из вставок, удалений и замен .

Расстояние Левенштейна можно определить как рекурсивную функцию или итеративную функцию. Итерационная версия Levensthein Distance быстрее рекурсивной версии благодаря динамическому программированию.

Рекурсивная версия:

def levenshteinRecursive(seq1, seq2):

    if seq1 == "":

        return len(seq2)
    if seq2 == "":

        return len(seq1)
    if seq1[-1] == seq2[-1]:
        cost = 0
    else:
        cost = 1

    res = min([levenshteinRecursive(seq1[:-1], seq2) + 1,
               levenshteinRecursive(seq1, seq2[:-1]) + 1,
               levenshteinRecursive(seq1[:-1], seq2[:-1]) + cost])
    return res

Динамическая версия:

def levenshtein(seq1, seq2):
    size_x = len(seq1) + 1
    size_y = len(seq2) + 1
    matrix = np.zeros ((size_x, size_y))
    for x in range(size_x):
        matrix [x, 0] = x
    for y in range(size_y):
        matrix [0, y] = y

    for x in range(1, size_x):
        for y in range(1, size_y):
            if seq1[x-1] == seq2[y-1]:
                matrix [x,y] = min(
                    matrix[x-1, y] + 1,
                    matrix[x-1, y-1],
                    matrix[x, y-1] + 1
                )
            else:
                matrix [x,y] = min(
                    matrix[x-1,y] + 1,
                    matrix[x-1,y-1] + 1,
                    matrix[x,y-1] + 1
                )

    return (matrix[size_x - 1, size_y - 1])

Заказанная версия расстояния Левенштейна :

Нам нужна заказанная версия Levensthein Distance, потому что некоторые названия продуктов могут быть заказаны иначе, как в приведенном выше примере.

def sorted_levenshtein_rate(seq1, seq2):
    product1 = ''.join(sorted(seq1))
    product2 = ''.join(sorted(seq2))
    distance = levenshtein(product1, product2)
    max_len = max(len(product1), len(product2))
    return 1-(distance/max_len)

def levenshtein_rate(product1, product2):
    distance = levenshtein(product1, product2)
    max_len = max(len(product1), len(product2))
    return 1 - (distance / max_len)


if __name__ == "__main__":
    met1 = 'Arko Tıraş Köpüğü 200 ml Cool'
    met2 = 'Arko Men Cool Tıraş Köpüğü 200 ml'
    print('Levenshtein Distance: {}, MatchScore: {} '.\
          format(levenshtein(met1, met2), levenshtein_rate(met1, met2)))
#    print('Sorted Levenshtein Distance: ', sorted_levenshtein(met1, met2))
    print('Sorted Levenshtein Distance: {}, MatchScore: {} '.\
          format(sorted_levenshtein(met1, met2), sorted_levenshtein_rate(met1, met2)))

Результат примера выше:
Расстояние Левенштейна: 14,0, MatchScore: 0,57
Отсортированное расстояние Левенштейна: 4,0, MatchScore: 0,88

Проблемы с мерами сопоставления строк:

Прежде всего, эти меры могут использоваться только как метрики. Использование только этой меры с системой, основанной на правилах, или простой моделью может привести к значительному снижению точности. Пример ниже покажет больше, чем было написано ранее.

if __name__ == "__main__":
    met1 = "Selpak Kağıt Havlu 8'li"
    met2 = "Selpak Kağıt Havlu 12'li"
    print('Levenshtein Distance: {}, MatchScore: {} '.\
          format(levenshtein(met1, met2), levenshtein_rate(met1, met2)))
#    print('Sorted Levenshtein Distance: ', sorted_levenshtein(met1, met2))
    print('Sorted Levenshtein Distance: {}, MatchScore: {} '.\
          format(sorted_levenshtein(met1, met2), sorted_levenshtein_rate(met1, met2))

Результат приведенного выше кода:
Расстояние Левенштейна: 2,0, MatchScore: 0,916
Сортированное расстояние Левенштейна: 2,0, MatchScore: 0,916

Действительно высокий показатель соответствия, который больше, чем в первом примере, но, в отличие от первого примера, эти 2 продукта не совпадают. Эти проблемы возникают из-за разных размеров порций, поскольку значения чисел больше, чем длина их строковой версии. Так что должно быть сделано?

Разработка функций в наборе данных

Сопоставление чисел следует определять с помощью другого столбца. Сначала мы извлечем список чисел в каждой строке в виде списка и преобразуем их в заданный объект. Затем мы можем получить меру совпадения чисел с len перехвата двух наборов за len объединения двух наборов. Вы можете найти функцию удара.

import re
def get_rate(row):
    product1 = row.externalName
    product2 = row.GetirName
    regex = r'[0-9]+'
    numbers1 = set(re.findall(regex, product1))
    numbers2 = set(re.findall(regex, product2))
    union = numbers1.union(numbers2)
    intersection = numbers1.intersection(numbers2)
    if len(numbers1)==0 and len(numbers2) == 0:
        rate = 1
    else:
        rate = (len(intersection)/ len(union))
    return rate

Я также считаю, что это уникальное число, состоящее из двух строк, что означает len перехвата наборов. Вы также можете найти код этой функции ниже.

import re
def get_unique_number_count(row):
    product1 = row.externalName
    product2 = row.GetirName
    regex = r'[0-9]+'
    numbers1 = set(re.findall(regex, product1))
    numbers2 = set(re.findall(regex, product2))
    union = numbers1.union(numbers2)
    return len(union)

Прогнозирование совпадений с машинным обучением (персептрон, логистическая регрессия, машины опорных векторов, многослойный персептрон)

Эта статья в основном не о машинном обучении. Алгоритмы будут использоваться без определения. Давайте сначала извлечем функции.

df= pd.read_excel('getir.xlsx', sheet_name='getir')

df['levenshteinDistance'] = df.apply(fe.sorted_levenshtein_apply, axis=1)
df['uniqueNumberCount'] = df.apply(fe.get_unique_number_count, axis=1)+1
df['numberMatchRate'] = df.apply(fe.get_rate, axis=1)
df['matchScore'] = df.apply(fe.sorted_levenshtein_rate_apply, axis=1)
df['normalizedMatchRate'] = (df['numberMatchRate']+2).apply(np.log)
df['squaredPriceRate'] = df['priceRate']* df['priceRate']
X = df[['matchScore', 'squaredPriceRate', 'uniqueNumberCount', 'normalizedMatchRate']].values
y = df['Match'].values

Как вы можете видеть в приведенном выше коде, мы сначала получаем данные, вычисляем новые функции и нормализуем с помощью журнала или расширяем возможности с помощью функции квадрата.

X = df[['matchScore', 'squaredPriceRate', 'uniqueNumberCount', 'normalizedMatchRate']].values
y = df['Match'].values

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20,\
                                                    random_state=4, stratify=y)
sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)

Выбираются столбцы функций, а данные делятся на обучающие и тестовые наборы.

import pandas as pd
import re
import numpy as np
import featureExtractors as fe
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Perceptron
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import classification_report
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=4, stratify=y)

Объект словаря Python, представленный ниже, даст нам определения всех моделей, которые мы использовали для классификации.

models = {'Perceptron' : Perceptron(max_iter=40, eta0=0.1, random_state=1),
 'LogisticRegression' : LogisticRegression(C=100.0, random_state=1),
 'LinearSVC' : SVC(kernel='linear', C=1.0, random_state=1),
 'KernelizedSVC' : SVC(kernel='rbf', random_state=1, gamma=5.0, 
C=1.0),
 'MLP' : MLPClassifier(activation='relu', alpha=0.0001, batch_size='auto', beta_1=0.9,
       beta_2=0.999, early_stopping=False, epsilon=1e-08,
       hidden_layer_sizes=(20, 20, 20), learning_rate='constant',
       learning_rate_init=0.001, max_iter=500, momentum=0.9,
       nesterovs_momentum=True, power_t=0.5, random_state=None,
       shuffle=True, solver='adam', tol=0.0001, validation_fraction=0.1,
       verbose=False, warm_start=False)
}

Эта статья не только о машинном обучении. Можно искать в Интернете все модели классификации и параметры, которые я использовал. Следующий фрагмент кода применит к набору данных все модели, указанные выше.

for model_name, model in models.items():
    model.fit(X_train_std, y_train)
    y_pred = model.predict(X_test_std)
    print(model_name)
    print('Accuracy: %.2f' % accuracy_score(y_test, y_pred))
    print('Fscore: %.2f' % fbeta_score(y_test, y_pred, beta=1))

Вывод фрагмента кода:

Персептрон:
Точность: 0,98
Оценка по шкале F: 0,99

LogisticRegression:
Точность: 0,98
Fscore: 0,99

LinearSVC:
Точность: 0,98
Fscore: 0,99

KernelizedSVC
Точность: 0,95
Fscore: 0,97

MLP:
Точность: 1.00
Fscore: 1.00

Распечатаны все значения точности и Fscores. Как видите, MLP выиграла конкурс. Некоторые могут прогнозировать все совпадения с помощью многослойного перцептрона. Вы можете найти набор данных и коды в моем аккаунте на github.

Спасибо за прочтение.