Недавно я столкнулся с очень распространенной проблемой в управлении данными — нормализацией. Большинство аналитиков предпочли бы найти и заменить в Excel. В моем списке желаний было попытаться решить проблему с большими наборами данных, которые превышают возможности Excel. Я использовал OpenRefine, инструмент, который предоставляет графический интерфейс и возможность выбирать различные уровни строгости для создания кластеров сходства, а затем присваивать значение класса этим кластерам. Однако этот подход является приземленным, трудоемким и прямо-таки скучным, поэтому подвержен человеческим ошибкам.
Я экспериментировал с созданием классификатора KNN и сравню его с нейронной сетью с использованием Tensorflow в своей следующей статье.
Постановка задачи
Учитывая набор данных с опечатками, перемешанными или отсутствующими символами, расшифруйте нормализованное имя, чтобы обеспечить качество данных.
Набор данных
Чтобы смоделировать это упражнение, я выбрал набор данных розничных продавцов, загрузив список 100 крупнейших розничных продавцов в США. Я создал кучу методов для приписывания случайных символов и чисел. Чтобы сделать его действительно расплывчатым, я также рандомизировал количество импутаций для одной строки, просто чтобы гарантировать, что данные не станут невозможными для понимания, я ограничил количество импутаций от 0 до 50% длины строки.
import string import random import numpy as np import pandas as pd # Using seen hash table to generate all unique values # Main goal here is to ensure there is no cross category contamination seen = {} def gen_impurities(n,word): for i in range(n): index = random.randint(0,len(word)-1) if index%2 == 0: symbol = random.choice(string.punctuation) word = word[:index] + symbol + word[index:] if index%7 == 0: num = random.choice(string.digits) #print(f"Number = {num}") word = word[:index] + str(num) + word[index:] if index%3 == 0: word.replace(word[index],"") return word def obfuscate(word): n = int(len(word)*0.5) iteration = random.randint(1,n) label = word for i in range(5): imputed_word = gen_impurities(iteration,label) if imputed_word not in seen: seen[imputed_word] = 1 return imputed_word seen[imputed_word] = seen[imputed_word]+1 return word def gen_gibberish(min_l,max_l): l = random.randint(min_l,max_l) res = ''.join(random.choices(string.ascii_uppercase + string.punctuation + string.digits, k=l)) while res not in seen: seen[res] = 1 return res seen[res] = seen[res]+1 def get_obfuscated_targets(targets,n): obfuscate_target = targets * n obfuscate_targets.sort() df_obfuscated = pd.DataFrame({"targets": obfuscate_targets}) df_obfuscated['bad_names'] = df_obfuscated["targets"].apply(lambda x: obfuscate(x) ) return df_obfuscated def get_catch_gibberish(instance_count,min_l,max_l): others = ['Other'] * instance_count df_other = pd.DataFrame({'targets':others}) df_other['bad_names'] = df_other['targets'].apply(lambda x : gen_gibberish(min_l,max_l)) return df_other def get_good_names(targets,n): good_captures = targets* n df_good_captures = pd.DataFrame({'targets':good_captures}) df_good_captures['bad_names'] = df_good_captures["targets"] return df_good_captures def get_data(impute_n,instance,min_l,max_l): n = impute_n instance_count = instance min_l = min_l max_l = max_l df_obfuscated = get_obfuscated_targets(targets,n) df_other = get_catch_gibberish(instance_count,min_l,max_l) df_good_captures = repeat_good_names(targets,n,instance_count) df = pd.concat([df_obfuscated,df_good_captures,df_other]) return df
K-ближайший сосед
Алгоритм K-ближайших соседей (KNN) — это популярный и интуитивно понятный алгоритм классификации и регрессии в машинном обучении. Это непараметрический алгоритм, работающий по принципу подобия. Алгоритм KNN классифицирует или прогнозирует новую точку данных, сравнивая ее с помеченными точками данных в обучающем наборе данных. Алгоритм измеряет расстояние между новой точкой данных и ее K ближайшими соседями в пространстве признаков. K ближайших соседей определяются на основе метрики расстояния, обычно евклидова расстояния. После определения соседей алгоритм присваивает метку класса большинства для классификации или вычисляет среднее значение для регрессии. KNN — это простой, но мощный алгоритм, который можно использовать для различных задач, таких как системы рекомендаций, классификация изображений и обнаружение аномалий. Однако важно выбрать подходящее значение для K и учесть влияние масштабирования признаков на достижение оптимальных результатов с помощью алгоритма KNN.
Классы
В этом конкретном случае использования есть 100 классов + 1 класс тарабарских строк или класс мусора, чтобы гарантировать, что какая-то случайная строка не классифицируется как допустимый класс. Я следовал стандартной практике разделения набора данных в тесте и обучении с использованием разделения 80:20.
def read_data(): df = pd.read_csv("Targets.csv") del df['empty'] df = df.set_index('no') df.head() return df def get_train_test_data(split_ratio,impute_ratio,instance,min_l,max_l): test_size = (instance*split_ratio) train_size = (instance*(1-split_ratio)) print(f"test_size = {test_size} & train_size = {train_size} & split = {split_ratio}") test_impute_n = int(test_size*impute_ratio) test_good_n = int(test_size - test_impute_n) print(f"test impute = {test_impute_n} & instances = {test_good_n}") df = read_data() targets = list(df["target"]) # get imputed df_obfuscated = get_obfuscated_targets(targets,test_impute_n) # get good names df_good_names = get_good_names(targets, test_good_n) # get others df_other = get_catch_gibberish(test_impute_n+test_good_n,min_l,max_l) df_test = pd.concat([df_obfuscated,df_good_names,df_other]) del df_obfuscated,df_good_names,df_other train_impute_n = int(train_size*impute_ratio) train_good_n = int(train_size - train_impute_n) # get imputed df_obfuscated = get_obfuscated_targets(targets,train_impute_n) # get good names df_good_names = get_good_names(targets, train_good_n) # get others df_other = get_catch_gibberish(train_impute_n+train_good_n,min_l,max_l) df_train = pd.concat([df_obfuscated,df_good_names,df_other]) del df_obfuscated,df_good_names,df_other return df_train, df_test def get_train_test_split(df_train,df_test,one_hot_encode_labels=False): X_train = df_train["bad_names"].values y_train = df_train["targets"].values X_test = df_test["bad_names"].values y_test = df_test["targets"].values if one_hot_encode_labels: df_labels = pd.concat([df_train['targets'],df_test['targets']]) print(f"Labels = {df_labels.shape})") lables = pd.get_dummies(df_labels) lookup = list(lables.columns) print(len(lookup)) del df_labels y_test_labels = pd.get_dummies(y_test) y_test_encoded = y_test_labels.astype('float32').values y_train_labels = pd.get_dummies(y_train) y_train_encoded = y_train_labels.astype('float32').values return X_train,y_train_encoded,X_test,y_test_encoded, lookup else: return X_train, y_train, X_test, y_test, None split_ratio = 0.2 impute_ratio = 0.5 instances = 50 min_l = 5 max_l = 20 df_train, df_test = get_train_test_data(split_ratio, impute_ratio, instances, min_l, max_l) X_train, y_train,X_test, y_test, lable_lookup = get_train_test_split( df_train, df_test, one_hot_encode_labels=False)
Векторизация текста:
Здесь я решил использовать векторизатор Tfidf в форме обучения scikit, предполагая, что комбинация двух символов может дать лучшее отношение к прогнозированию целей. TfidfVectorizer — это метод извлечения текстовых объектов, который преобразует набор текстовых документов в числовое представление. TF-IDF расшифровывается как Term Frequency-Inverse Document Frequency, что является широко используемой статистической мерой при поиске информации и анализе текста.
Я подгоняю векторизатор к моему корпусу плохих имен, который генерирует n-граммы для длины 2 (2-грамма). Затем я преобразовываю каждое наблюдение, применяя векторизацию для получения преобразованного массива.
from sklearn.feature_extraction.text import TfidfVectorizer Text = "".join(X_train) vectorizer = TfidfVectorizer(analyzer='char',strip_accents ="ascii" , ngram_range=(2,2)).fit(Text) X_train_vector = vectorizer.transform(X_train).toarray() X_test_vector = vectorizer.transform(X_test).toarray()
Классификатор К-НН
Важно отметить, что алгоритм KNN не включает обучение модели, поскольку это алгоритм обучения на основе экземпляров. Ниже приведен фрагмент, используемый для создания классификатора.
from sklearn.neighbors import KNeighborsClassifier from scipy.spatial import distance classifier = KNeighborsClassifier( n_neighbors=20, n_jobs=10, metric = distance.correlation) classifier.fit(X_train_vector, y_train)
Вот разбивка того, как классификатор будет работать с заданными параметрами:
n_neighbors=20
: этот параметр определяет количество соседей, рассматриваемых для классификации. В этом случае классификатор будет рассматривать 20 ближайших соседей к точке запроса.n_jobs=10
: этот параметр указывает количество параллельных заданий, которые будут выполняться во время поиска соседей. Это позволяет использовать параллельную обработку для ускорения вычислений. В этом случае классификатор будет использовать 10 параллельных заданий для поиска соседей.metric=distance.correlation
: этот параметр определяет показатель расстояния, используемый для расчета расстояния между точками данных. Метрикаdistance.correlation
представляет собой корреляционное расстояние, которое измеряет несходство между двумя образцами на основе их коэффициента корреляции. Это допустимая метрика для алгоритмов KNN.
Учитывая новую точку данных, классификатор выполнит следующие шаги:
- Вычислить расстояния: классификатор вычисляет корреляционное расстояние между новой точкой данных и всеми другими точками данных в обучающем наборе.
- Выбрать соседей: выбирает 20 ближайших соседей на основе вычисленных корреляционных расстояний.
- Классифицировать точку данных: для классификации метка класса большинства среди выбранных соседей назначается в качестве прогнозируемой метки для новой точки данных.
Решение о классификации принимается на основе помеченных точек данных в обучающем наборе. Указанные параметры в экземпляре KNeighborsClassifier
влияют на количество учитываемых соседей, параллельную обработку и используемую метрику расстояния.
Получение прогнозов
Как упоминалось выше, обучение не требуется, и большая часть тяжелой работы выполняется на стороне предсказания. Классификатору по существу нужны все обучающие экземпляры, чтобы принять решение.
def my_classifier(w:list,vectorizer,classifier): w_vector = vectorizer.transform(w).toarray() pred = classifier.predict(w_vector) return pred
Данные, которые я использовал, относились к ведущим ритейлерам, которые функции прогнозирования могут предсказать, как показано ниже:
test_stores = ["Targe1", "Larget", "!a7g3T", "'7argay'", "7- 11", "Ac3 Hw"] my_classifier(test_stores ,vectorizer,classifier) OP : array(['Target', 'Target', 'Other', 'Gap', '7-Eleven', 'Ace Hardware'], dtype=object)
Как видно выше, модель хорошо справляется с назначением наиболее релевантного названия магазина.
Оценка модели
from sklearn.metrics import classification_report from sklearn.metrics import confusion_matrix from sklearn.metrics import accuracy_score conf_mat = pd.DataFrame({'test_names':X_test,'actual':y_test}) conf_mat["predicted"] = conf_mat["test_names"].apply( lambda x : my_classifier([x], vectorizer, classifier)[0]) result = accuracy_score( conf_mat['actual'].values, conf_mat['predicted'].values) print("Accuracy:",result) OP: Accuracy: 0.9910891089108911
Обучение
- Классификатор KNN будет потреблять много памяти по мере роста размера обучающих данных.
- Количество классов и размер данных также будут влиять на производительность предиктора.
- Может быть точным с меньшими тренировочными наборами.
- Чтобы выбрать правильную функцию расстояния и количество соседей, потребуются пробы и ошибки с некоторыми обоснованными предположениями.
- Лучше всего оптимизировать производительность классификатора с наименьшим количеством классов и размером обучающих данных.
- KNN Classifier не требует горячего кодирования меток и может предсказывать метки классов.
- Оценка займет время, пропорциональное количеству записей в тестовом наборе.
- Может быть эффективным для получения одиночных прогнозов.
Оформить блокнот на Github