Изграждане на невронна мрежа с вграждания за препоръки за филми

Съвместното филтриране е инструмент, който компаниите използват все повече. Netflix го използва, за да препоръчва предавания, които да гледате. Facebook го използва, за да препоръча с кого трябва да бъдете приятели. Spotify го използва, за да препоръчва плейлисти и песни. Това е невероятно полезно при препоръчване на продукти на клиенти.

В тази публикация изграждам невронна мрежа за съвместно филтриране с вграждания, за да разбера как потребителите ще се чувстват към определени филми. От това можем да им препоръчаме филми за гледане.

Наборът от данни е взет от тук. Този код е базиран на „fastai notebook“.

Първо, нека се отървем от досадно сложните потребителски идентификатори. Можем да се справим с обикновени стари цели числа. Те са много по-лесни за работа.

import pandas as pd
ratings = pd.read_csv('ratings.csv')
movies = pd.read_csv('movies.csv')

След това ще направим същото и за идентификаторите на филми.

u_uniq = ratings.userId.unique()
user2idx = {o:i for i,o in enumerate(u_uniq)}
ratings.userId = ratings.userId.apply(lambda x: user2idx[x])

Ще трябва да получим броя на потребителите и броя на филмите.

n_users=int(ratings.userId.nunique())
n_movies=int(ratings.movieId.nunique())

Първо, нека създадем някои произволни тегла. Трябва да се обадим. Това ни позволява да избегнем изричното извикване на базовия клас. Това прави кода по-поддържаем.

Тези тегла ще бъдат равномерно разпределени между 0 и 0,05. Операторът _ в края на uniform_ обозначава операция на място.

След това добавяме нашите матрици за вграждане и латентни фактори.

Създаваме матрица за вграждане за нашите потребителски идентификатори и идентификатори на филми. Вграждането е основно търсене на масив. Когато умножим нашите еднократно кодирани потребителски идентификатори по нашите тегла, повечето изчисления се анулират до 0 (0 * number = 0). Всичко, което ни остава, е конкретен ред в матрицата на теглото. Това е основно „просто търсене на масив“.

Така че нямаме нужда от умножение на матрицата и нямаме нужда от еднократно кодиран масив. Вместо това можем просто да направим търсене в масив. Това „намалява използването на паметта“ и ускорява невронната мрежа. Той също така разкрива присъщите свойства на категоричните променливи. Тази идея беше приложена в скорошно „състезание на Kaggle“ и „постигна 3-то място“.

Размерът на тези матрици за вграждане ще се определя от n_фактори. Тези фактори определят броя на латентните фактори в нашия набор от данни.

„Латентните фактори“ са изключително полезни в нашата мрежа. Те намаляват необходимостта от инженеринг на функции. Например, ако User_id 554 харесва Tom cruise и Tom cruise се появява във филм. Потребител 554 вероятно ще хареса филма. Появата на Tom cruise във филм би била латентна функция. Не го уточнихме преди тренировка. Просто се появи. И се радваме, че го направи.

И накрая, ще трябва да добавим нашата функция forward.

Както подсказва името на този клас, ние правим точков продукт на вграждане на матрици.

users,movies = cats[:,0],cats[:,1] ни дава минипартида потребители и филми. Разглеждаме само категорични променливи за вграждане. conts се отнася за непрекъснати променливи.

Размерът на тази минипартида ще се определя от размера на партидата, който сте задали. Според „този“ документ големият размер на партидата може действително да компрометира качеството на модела. Но според „този“ документ големият размер на партидата повишава качеството на модела. Към момента няма консенсус. Много хора съобщават за „противоречиви резултати“. Така че не се колебайте да експериментирате с размер на партида по ваш избор.

От тази минипартида искаме да направим търсене на масив в нашата матрица за вграждане.

self.u(users),self.m(movies) ни позволява да направим това търсене в масив. Това търсене е по-малко изчислително интензивно от матрично умножение на еднократно кодирана матрица и тегловна матрица.

(u*m).sum(1).view(-1, 1) е кръстосано произведение на вгражданията за потребители и филми и връща едно число. Това е прогнозираният рейтинг за този филм.

След това трябва да създадем ColumnarModelData обект

След това ще настроя оптимизатор. Ще използвам стохастичен градиентен низход за това. optim.SGD прилага стохастичен градиентен низход. Стохастичното градиентно спускане е изчислително по-малко интензивно от градиентното спускане. Това е така, защото въвеждаме произволност, когато избираме точка от данни за изчисляване на производната.

Можем също да използваме optim.Adam. Това изпълнява rmsprop и momentum. На свой ред това води до адаптивна скорост на обучение. Но „тази“ статия показва, че решенията, получени от SGD, обобщават много по-добре от решенията, получени от Адам. Освен това тренировката не отнема много време, така че SGD не е лош вариант.

Тогава се побираме за 3 епохи.

fit(model, data, 3, opt, F.mse_loss)

Загубата на MSE е просто загуба на средна квадратична грешка. Това се изчислява автоматично.

Fastai създава невронна мрежа автоматично зад кулисите. Можете да извикате collab_learner, което автоматично създава невронна мрежа за съвместно филтриране. Fastai също има опции за въвеждане на „пристрастност“ и „отпадане“ чрез този обучаващ се в сътрудничество.

Пристрастието е много полезно. Трябва да открием пристрастията на потребителите и филмите. Потребителските пристрастия биха отчели хората, които дават високи оценки за всеки филм. Филмовите пристрастия биха отчитали хората, които са склонни да дават високи оценки за определен тип филми. Fastai автоматично добавя Bias.

Използвайки fastai, можем лесно да създадем обучаем в сътрудничество:

Пристрастието е много полезно. Трябва да открием пристрастията на потребителите и филмите. Потребителските пристрастия биха отчели хората, които дават високи оценки за всеки филм. Филмовите пристрастия биха отчитали хората, които са склонни да дават високи оценки за определен тип филми. Fastai автоматично добавя Bias.

Интересното е, че Fastai отбелязва, че трябва да увеличите y_range леко. Използва се сигмоидна функция, за да се гарантира, че крайният резултат е между числата, посочени в y_range. Проблемът е, че сигмоидната функция е асимтотна. Така че ще трябва леко да увеличим нашето y_range. Fastai препоръчва увеличаване с 0.5.

Тук използвам предложената скорост на обучение с малко намаляване на теглото. Това е комбинацията, която открих, че работи много добре.

Можем да тренираме още

Най-накрая получаваме MSE от 0.784105. Но това е много неравномерно пътуване. Загубата ни скача значително нагоре и надолу. Въпреки това 0.784105 всъщност е по-добър резултат от системата LibRec за съвместно филтриране. Те получаваха 0.91**2 = 0.83 MSE.

Освен това всъщност е малко по-добър от модела, създаден от fastai в техния „урок за съвместно филтриране“. Те получаваха 0.814652 MSE.

Подобрения

  1. Можем да коригираме размера на вграждането, като изпратим речник, наречен emb_szs. Това може да е полезен параметър за коригиране.
  2. Препоръка въз основа на съдържанието. Съвместното филтриране е само един метод за изграждане на система за препоръки. „Други методи“ могат да бъдат по-полезни. Система, базирана на съдържание, е нещо, което имам предвид. Това може да разгледа метаданни като актьорски състав, екип, жанр и режисьор, за да направи препоръки. Мисля, че някакво „хибридно“ решение би било оптимално. Това би комбинирало базирана на съдържание система за препоръки и система за съвместно филтриране.
  3. Съвместното филтриране е до голяма степен подкопано от „проблема със студения старт“. За да преодолеем това, бихме могли потенциално да разгледаме метаданните на потребителите. Например, можем да разгледаме неща като: пол, възраст, град, време, в което са имали достъп до сайта и т.н. Просто всички неща, които са въвели във формуляра за регистрация. Изграждането на модел върху тези данни може да бъде трудно, но ако работи добре, може да бъде полезно.

Първоначално публикувано на адрес https://spiyer99.github.io на 12 юли 2020 г.