Эндрю Бол, Эмили Баззелли, Хоуп Кнопф, Шерли Лу, Джонатан Эванс

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

Пытаясь решить эту проблему, Джонатан Эванс основал компанию под названием Reengr (произносится как звонарь). По своей сути Reengr — это технология, которая предсказывает, насколько вероятно, что люди понравятся друг другу. Это достигается с помощью 21 психологического вопроса и 5 социально-демографических вопросов, которые призваны создать всеобъемлющую картину личности пользователя. Однако психологические вопросы далеки от типичных психологических вопросов, которые вы могли бы увидеть в тесте Майерс-Бриггс или Большой пятерке. Reengr пытается уловить истинную природу человека, задавая такие вопросы, как Хотели бы вы когда-нибудь начать свой бизнес? и ты считаешь себя забавным?. Мы также попросили респондентов указать, резонируют ли вопросы с ними. Это должно было добавить еще один уровень глубины опросу, но в итоге мы проигнорировали его в наших моделях. Однако мы можем вернуться к нему позже, чтобы проверить, оказывает ли он какое-либо существенное влияние на точность наших моделей. Если вы хотите увидеть полный список вопросов, вот ссылка на опрос: https://goo.gl/forms/PXTMZVe1ZoQfReIp2

Чтобы создать начальные прогностические модели для Reengr, нам понадобилась переменная y. Это пришло в форме вопроса «лучший друг (и) / романтический партнер (ы)». В начале опроса респондента просят ввести свое имя и первые две буквы своей фамилии и сделать то же самое для всех своих лучших друзей и своего нынешнего романтического партнера (или партнеров, если они предприимчивы). Это позволило нам иметь положительный класс. Мы знали, кто определенно нравится людям, и это было для нас отличным началом. К сожалению, это создало проблему. Мы понятия не имели, кто людям не нравится. По сути, у нас не было отрицательного класса. Это привело нас на путь позитивного и немаркированного обучения.

Положительное немаркированное обучение

Позитивное немаркированное обучение названо именно по этой причине: модели подходят только для положительных и немаркированных значений. Значение считается немаркированным, если метка положительного или отрицательного значения неизвестна. В нашем проекте в опросе просили указать ваших нынешних друзей, но никогда не просили перечислить людей, с которыми вы не дружите. Из-за того, как был создан опрос, мы присвоили значение 1 (положительное) известным парам друзей и значение -1 (без метки) всем, кто не упоминается явно. Значение 0 было сохранено для отрицательного класса, но не было введено до более позднего моделирования.

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

Двухэтапный

Наш первый подход к работе с сильно несбалансированными данными заключался в использовании двухэтапного подхода к дереву решений. Этот подход был опробован на индивидуальной основе и, таким образом, замедлил процесс поиска совпадений для каждого человека. Однако для одного пользователя процесс выполняется относительно быстро. Для объяснения двухэтапного подхода мы создадим пример пользователя А, который явно перечислил своих друзей как H, E, J и S.

Во-первых, был создан новый фрейм данных, включающий всех пользователей, кроме рассматриваемого пользователя, с переменной Y, равной 1, 0 или -1, в зависимости от статуса известного соответствия или отсутствия для начала.
Шаг 1 заключался в том, чтобы обучить дерево решений на этом исходном фрейме данных, а затем предсказать вероятность увидеть 1 для нашей переменной Y. В нашем примере строки, представляющие пользователей H, E, J и S, будут иметь 1 в качестве переменной Y, а все остальные будут начинаться с -1.

Шаг 2 включал создание новых меток для неизвестных совпадений следующим образом; для всех известных положительных результатов мы нашли диапазон вероятностей, предсказанный деревом. В нашем примере, если прогнозируемые вероятности увидеть положительный результат были следующими H: 0,6, E: 0,55, J: 0,7, S: 0,8, диапазон будет рассчитан как [0,55, 0,8]. Теперь для всех неизвестных точек сравниваем прогнозируемую вероятность срабатывания с диапазоном. Любые значения ниже нижней границы диапазона считаются новым отрицательным значением, любая точка выше классифицируется как новое положительное значение, и если оно попадает в диапазон, оно остается неотмеченной точкой.
Наконец, мы заменяем начальные переменные Y с нашими новыми метками и продолжайте шаги 1 и 2, пока не будут классифицированы новые метки.

df = preproc()
   y = df["Friend?"]
   ys = 2*y - 1                    # pos = 1, unlabled = -1
   X = df.drop("Friend?", axis = 1)
def two_step(X,y,ys):
       rf = RandomForestClassifier(n_estimators = 1000)
       rf.fit(X, y)
pred = rf.predict_proba(X)[:,1]
       range_P = [min(pred * (ys > 0)), max(pred * (ys > 0))] # Min and Max values for known positives
iP_new = ys[(ys < 0) & (pred >= range_P[1])].index
       iN_new = ys[(ys < 0) & (pred <= range_P[0])].index
       ys.loc[iP_new] = 1
       ys.loc[iN_new] = 0
rf2 = RandomForestClassifier(n_estimators = 1000)
for i in range(10):
           # If step 1 didn't find new labels, end
           if len(iP_new) + len(iN_new) == 0 and i > 0:
               break
# Step 2
           # Retrain on new labels and get new scores
           rf2.fit(X, ys)
pred = rf2.predict_proba(X)[:,-1]
           range_P = [min(pred * (ys > 0)), max(pred * (ys > 0))]
# Repeat step 1
           iP_new = ys[(ys < 0) & (pred >= range_P[1])].index
           iN_new = ys[(ys < 0) & (pred <= range_P[0])].index
           ys.loc[iP_new] = 1
           ys.loc[iN_new] = 0
return ys,pred

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

Что делать?

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

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

Только позитивный метод

Для положительного метода мы ссылались на статью Изучение классификаторов только на положительных и неразмеченных данных (2008 г.) Элкана и Ното. Центральным предположением в этой статье является стандартный классификатор, обученный на положительных и неразмеченных примерах, эквивалентный обучению на положительных и отрицательных примерах.

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

Кластеризация

Кластеризация использовалась как метод EDA для понимания собранных данных. У этой техники были две основные цели. Во-первых, нужно было понять типы людей в наборе данных, сгруппировав их, а во-вторых, понять, какие типы людей в каждой группе указаны/предпочтительны в качестве друзей. Поскольку все данные были категоричными, K-Modes использовали алгоритм кластеризации. K-Modes определяет центроиды кластера на основе наиболее часто встречающейся комбинации признаков в наборе данных. Затем он определяет, находятся ли точки данных в кластере, исходя из того, сколько функций совпадает между функциями кластера и функциями наблюдения. Для получения дополнительной информации о K-Modes см.: https://shapeofdata.wordpress.com/2014/03/04/k-modes/. Никакого специального взвешивания для определения кластеров не использовалось (это означает, что каждый ответ, не являющийся общим между наблюдением и центроидом, подвергался одинаковому штрафу). Кроме того, отсутствующие значения в наборе данных считались информационными — это означало, что для любого наблюдения, которое имело нулевое значение для любого вопроса, была создана отдельная категория. Команда исходила из того, что кто-то, кто не ответил на вопрос, дал представление о характере/личности человека.

Чтобы подготовить данные для кластеризации, все ответы были вручную закодированы в числа, представляющие каждый из различных ответов респондентов. Закодированные ответы каждого наблюдения, а также закодированная демографическая информация были введены в алгоритм K-Modes, и было определено 7 кластеров.

#Initialize KModes
km = KModes(n_clusters=7, init='Huang', verbose=0, random_state = 526747)

#Fit cluster
clusters = km.fit_predict(new_data)

# Print the cluster centroids
clust = pd.DataFrame(km.cluster_centroids_, columns = col)

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

Далее «друзья каждого наблюдения» также были отнесены к своему кластеру. «Друзья каждого наблюдения» — это те наблюдения, которые человек указал в качестве одного из своих друзей. Чтобы отнести друга к кластеру, он также должен был пройти опрос. После того, как все наблюдения и их список друзей были отнесены к кластеру, количество раз, когда люди в каждом кластере указывали друга из каждого другого кластера, подсчитывалось и записывалось. Затем эти значения нормировались на общее количество перечисленных друзей для конкретного кластера.

#Count the number of times a cluster shows up for every other cluster & the total number of times a cluster appears as a friend
counts = {}
for key in clusterDict:
   key_count={}
   total_count = 0
   for item in clusterDict[key]:
total_count = total_count+1
       for cluster in list(data['labels'].drop_duplicates()):
if item == cluster:
               try:
                   key_count[cluster] = key_count[cluster] + 1
               except:
                   key_count[cluster] = 1
   key_count['total'] = total_count
   counts[key] = key_count
#Normalize result by the total number of people in each cluster
for key in counts:
   for item in counts[key]:
       if item != 'total':
           counts[key][item] = round(counts[key][item]/counts[key]['total']*100,2)

Наконец, для каждого кластера были нанесены нормализованные оценки друзей из списка. Полученные графики можно увидеть ниже:

Из этих кластеров можно сделать несколько обобщений. Следует отметить, что кластеры, в которых преобладают женщины (0, 1, 4, 5), склонны указывать членов своих кластеров в качестве своих лучших друзей, а это означает, что женщины, как правило, дружат с людьми, которые наиболее похожи на них в соответствии с наш алгоритм. С другой стороны, кластеры, состоящие в основном из мужчин, обычно дружат с другими кластерами с мужчинами, но у них низкие или нулевые баллы с людьми из их собственного кластера, а это означает, что мужчины, как правило, предпочитают друзей, которые не так похожи на них, как женщины.

Логистическая регрессия

Согласно статье «Друг, похожий на меня» в журнале Scientific American, люди выбирают себе друзей, которые больше похожи на них, когда у них есть выбор. Поэтому в логистической регрессии мы закодировали наши доступные данные (известные положительные совпадения) для каждой пары людей, чтобы увидеть, насколько похожи их ответы из опроса. Мы хотим предсказать, будут ли два человека соответствовать 1 как положительному и 0 как отрицательному. Приведенные баллы из метода PosOnly — это вероятность того, что два человека совпадут (друзья или кто-то другой). Пороговое значение было равно 0,5; если оценка была выше этого значения, то пара людей считалась с высокой вероятностью совпадающей, а если оценка была ниже, то считалось, что пара имеет низкую вероятность совпадения. совпадение, основанное на сходстве. Мы использовали разделение поездов 80/20 на наши данные. Мы начали с 20 истинных положительных результатов в нашем тестовом наборе, и логистическая регрессия сообщила о 20-кратном количестве положительных результатов, чем истинно помеченные положительные результаты.

# Import necessary modules
from sklearn.metrics import roc_curve, roc_auc_score
# Compute predicted probabilities: y_pred_prob
# y_pred_prob = logreg.predict_proba(X_test)[:,1]
a = obj.predict_proba(X_test)
B = np.reshape(a, (-1, 2))
y_pred_prob = B[:,1]
# Generate ROC curve values: fpr, tpr, thresholds
fpr, tpr, thresholds = roc_curve(y_test, a)
# Plot ROC curve
plt.plot([0, 1], [0, 1], 'k--')
plt.plot(fpr, tpr, label='%s ROC (area = .837)')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve for LogReg, AUC = .837')

Мы изучили другие показатели для оценки производительности модели и сослались на «Восстановление истинной производительности классификатора при позитивном обучении без маркировки» Шантану Джейна, Марты Уайт, Предрага Радивояка и обнаружили, что точность и ROC-кривая в положительном а немаркированное обучение обычно недооценивает реальную производительность, когда модель обучается на положительных и отрицательных данных.

Нейронные сети

Мы также запустили алгоритм нейронной сети, сохранив разделение обучения/тестирования и кодируя данные в соответствии с моделью логистической регрессии. Мы использовали классификатор Multilayer Perceptron Regressor и нашли оптимальную скорость обучения, количество скрытых слоев, импульс и т. д. На самом деле нейронная сеть работала немного хуже, чем логистическая регрессия, что нас удивило. Однако нейронная сеть предсказывала больше положительных результатов, чем логистическая регрессия, поэтому в этом смысле она работала лучше.

clf = MLPClassifier(hidden_layer_sizes=(10,), max_iter=500, random_state=50,learning_rate_init=0.0001,  learning_rate='constant', momentum=0.9)
estimator = clf.fit(X_train,y_train)

Заключение

Этика

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

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

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

Перейдите по следующей ссылке, чтобы узнать больше об этических проблемах в науке о данных: http://deon.drivendata.org/examples/

Выводы

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

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

Следующие шаги

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