Если вы хотите подписаться на этот блог - откройте блокнот Google Colab [TigerGraph с Pytorch.ipynb - Colaboratory (google.com)]

Вступление:

В этом блоге мы рассмотрим демонстрацию использования Pytorch с выводом графика для прогнозирования того, понравится ли фильм пользователю. Данные собираются из базы данных TigerGraph с помощью PyTigergraph (пакет Python). Данные собираются с помощью вызова REST по пользовательскому запросу. Результат вызова поступает в записную книжку в формате JSON. Используя Pandas, мы можем преобразовать эти данные в фрейм данных. После преобразования данных мы готовим данные для базовой модели машинного обучения с помощью фреймворка Pytorch.

В этом блоге вы узнаете, как создать решение MyGraph с помощью TigerGraph, как извлекать данные с помощью GSQL, а также как построить модель машинного обучения с использованием инфраструктуры Pytorch. Давайте начнем!

Обсуждаемые разделы:

Часть I. Создание (бесплатного) решения TigerGraph на https://tgcloud.io/

Часть II: Настройка ноутбука

Часть III: Предварительная обработка данных

Часть IV: Настройка Pytorch Framework

Часть V: обучающий набор и набор для тестирования

Часть VI: Построение модели

Часть VII: Оптимизация модели

Часть VIII: Оценка модели

Часть I. Создание (бесплатного) решения TigerGraph на https://tgcloud.io/

Во-первых, вам нужно создать бесплатное решение по адресу https://tgcloud.io /. Это облачная платформа, на которой мы будем размещать нашу базу данных графиков. Перейдите на https://tgcloud.io/ и создайте учетную запись, если вы еще этого не сделали. Вы можете зарегистрироваться в Google, LinkedIn или по электронной почте.

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

Для начала вам понадобится экземпляр облака TigerGraph с Recommendation Engine (Movie Recommendation) v3.1.1 Starter Kit. Не выбирайте пустой, потому что у вас не будет данных или модели.

Как только вы нажмете «Далее», мы увидим настройки экземпляра, как показано ниже. Просто оставьте настройку по умолчанию.

Что касается настроек решения, вы можете изменить имя решения, а также начальный пароль. В общем, мы рекомендуем установить пароль «тигрограф».

Просто «Отправить», прежде чем двигаться дальше.

После того, как вы подготовили коробку с упомянутым выше стартовым комплектом, откройте GraphStudio.

В верхнем левом углу вы увидите Global View, и вы увидите что-то вроде этого, прежде чем двигаться дальше:

Щелкните по нему и выберите MyGraph. Как только вы выберете MyGraph, глобальный вид исчезнет, ​​и он будет выглядеть аналогично этому.

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

После завершения загрузки данных у вас будет:

Идеально. Теперь у вас Graph UP, и ваши данные ЗАГРУЖЕНЫ. Давайте двигаться дальше.

Часть II: Настройка ноутбука

Шаг I. Нам понадобятся следующие пакеты, установленные на вашем Google Colab. PyTigerGraph - это коннектор, который мы будем использовать для взаимодействия с TigerGraph.

#we install these packages in the google colab
!pip install pyTigerGraph
!pip install pandas
!pip install flat-table
!pip install LibTorch
!pip install -q sklearn
!pip install torch

Затем нам нужно импортировать и реализовать эти пакеты, которые мы установили ранее.

#since we install these packages previously, then we can import and use these packages now
import matplotlib.pyplot as plt
import pyTigerGraph as tg
import pandas as pd
import numpy as np
import torch as pt
import flat_table
from __future__ import absolute_import, division, print_function, unicode_literals
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris

Шаг II: настройка подключения сервера к ноутбуку

Теперь давайте настроим коробку с соответствующими настройками. Когда вы подготовили свой ящик, вы дали ему Уникальный URL. Вставьте этот URL в параметр host. Другой параметр, который вам нужно будет изменить, - это password. Как правило, пароль по умолчанию - tigergraph. Замените пароль тем паролем, который вы ввели в процессе подготовки. Запускаем ячейку. Мы распечатаем токен, чтобы убедиться, что соединение работает.

На моем личном хосте это будет выглядеть так:

#connect the google colab to the tigergraph icloud platform
##If it's in my personal case, I will have my host as below
conn = tg.TigerGraphConnection(host="https://401a53c7e81d45fdae57fb8ddccf3812.i.tgcloud.io/", graphname = "MyGraph", gsqlVersion="3.0.5", username="tigergraph", password="tigergraph", useCert=True)
secret = conn.createSecret()
token = conn.getToken(secret, setToken=True)
print(token)

Давайте проверим токен!

(‘7u8bgj84v8pt00i761r0d8t12tbcp8gk’, 1629604111, ‘2021–08–22 03:48:31’)

Вы подключены! Давайте воспользуемся подключением, чтобы увидеть, какие конечные точки существуют на коробке.

results = conn.getEndpoints()
print(results)

Вы увидите что-то вроде этого:

{‘DELETE /graph/{graph_name}/delete_by_type/vertices/{vertex_type}/’: {‘parameters’: {‘ack’: {‘default’: ‘all’, ‘max_count’: 1…

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

## feature extraction from gsql with features of userID, movieID, userRating, movie title, genre
conn.gsql(‘’’
USE GRAPH MyGraph
CREATE QUERY userData(VERTEX<person> p) FOR GRAPH MyGraph {
// Feature Extraction of person: movieID, movieTitle, userRating, term, termRating
// Sample Param = 271
SumAccum<float> @rating;
BagAccum<VERTEX> @user;
src = {p}; //From the User
S1 = SELECT tgt FROM src:s -(rate:e)-> movie:tgt //Grab all the movies that they rated
ACCUM tgt.@rating += e.rating,
tgt.@user += p;
//Also add a local varible of that users rating
PRINT S1[S1.@user as user, S1.title as movieTitle, S1.@rating as userRating, S1.genres as genre];
}
INSTALL QUERY userData
‘’’, options=[])

Примечание. Поскольку размеры всех идентификаторов пользователей слишком велики для извлечения, мы сосредоточимся на одном идентификаторе пользователя, которым является «118205», и будем рассматривать его как демонстрационный пример.

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

Часть III: Предварительная обработка данных

Теперь запрос установлен. Ноутбук подключен. Назовем созданную нами конечную точку для получения данных о человеке 118205

preInstalledResult = conn.runInstalledQuery(“userData”, {“p”:”118205"})
parsR = (preInstalledResult)
print(parsR) # full return of REST call

Тогда у нас есть информация о человеке 118205:

[{‘S1’: [{‘v_id’: ‘2163’, ‘v_type’: ‘movie’, ‘attributes’: {‘user’: [‘118205’], ‘movieTitle’: ‘Attack of the Killer Tomatoes! (1978)’, ‘userRating’: 2, ‘genre’: ‘Comedy|Horror’}}…

Далее нам нужно преобразовать структуру данных JSON.

df = pd.DataFrame(parsR[0][“S1”]) # Grab only the data we are returningdf 
# take a look at the data format

Тогда у нас есть:

Нормализуем данные в столбце атрибутов.

# Normalize Data
# regarded as a preprocessing step
df_t1 = flat_table.normalize(df)
df_t1[‘attributes.userRating’] = df_t1[‘attributes.userRating’]/5 # divinding by 5 to get a decimal rating which will be used in model
df_t1 # Output DataFrame

Тогда у нас есть:

Давайте немного очистим столбцы данных, переименовав их.

# Rename Columns
df_t2 = df_t1.rename(columns={
‘attributes.user’:’userID’,
‘v_id’:’ID’,
‘v_type’:’Type’,
‘attributes.movieID’:’movieID’,
‘attributes.movieTitle’:’movieTitle’,
‘attributes.userRating’:’userRating’,
‘attributes.genre’:’genre’
})
df_t2 # Output DataFrame

Тогда у нас есть:

Затем давайте разберем жанры на отдельные ряды.

# Exploding the genre types to seperate rows
data = {‘userID’: [], ‘ID’: [],’Type’: [], ‘movieTitle’: [],’genre’: [],’userRating’: []}
for i in df_t2.index:
genres = df_t2[“genre”][i].split(“|”)
for e in genres:
data[‘userID’].append(df_t2[‘userID’][i])
data[“ID”].append(df_t2[“ID”][i])
data[“Type”].append(df_t2[“Type”][i])
data[“movieTitle”].append(df_t2[“movieTitle”][i])
data[“genre”].append(e)
data[“userRating”].append(df_t2[“userRating”][i])
df_t2 = pd.DataFrame(data, columns = [‘userID’,’ID’,’Type’,’movieTitle’,’genre’, ‘userRating’])
print(df_t2)

Тогда у нас есть:

Идеально. Теперь давайте перейдем к жанрам и превратим их в столбцы.

# Pivoting the genres into coulmn headers
df_t3 = df_t2.pivot(index=’ID’, columns=’genre’, values=’userRating’)
df_t3 = pd.DataFrame(df_t3, columns = [‘Action’, ‘Adventure’, ‘Animation’, ‘Children’, ‘Comedy’, ‘Crime’, ‘Documentary’, ‘Drama’, ‘Fantasy’, ‘Film-Noir’, ‘Horror’, ‘IMAX’, ‘Musical’, ‘Mystery’, ‘Romance’, ‘Sci-Fi’, ‘Thriller’, ‘War’, ‘Western’])
df_t3 = df_t3.fillna(0)
df_t3 # Output DataFrame

Тогда у нас есть:

Давайте объединим фреймы данных и посмотрим, что у нас получится.

# Put dataframes together
df_t4 = pd.merge(df_t2, df_t3, how=’outer’, on=[‘ID’])
df_t5 = df_t4.drop(columns=[‘genre’])
df_t6 = df_t5.drop_duplicates(subset =”ID”)
df_t6 # Output DataFrame

Тогда у нас есть датафрейм, который выглядит так:

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

## extract only userID, movieID and userRating
## there are 19590 observations for user “118205”
df_features = df_t2[[‘userID’, ‘ID’, ‘userRating’]]
df_features

Тогда у нас есть:

Часть IV: Настройка Pytorch Framework

Давайте настроим фреймворк Pytorch

##installing torchivision and importing torch
!pip install torchvision
import torch
import torchvision

Тогда у нас есть:

Часть V: обучающий набор и набор для тестирования

Давайте разделим набор данных на обучающий набор и набор для тестирования соответственно. 80% для обучающей выборки и 20% для тестовой.

#splitting into 80% for the training set and the 20% for the testing set
train_size = int(0.8 * len(df_features))
test_size = len(df_features) — train_size
train_dataset, test_dataset = torch.utils.data.random_split(df_features, [train_size, test_size])

Давайте посмотрим на количество наблюдений в обучающей и тестовой выборках соответственно.

## numbers of observation in training set
train_size

Тогда у нас есть:

15672

Что касается количества наблюдений в тестовой выборке:

## numbers of observation in testing set
test_size

Тогда у нас есть:

3918

Давайте превратим набор для обучения и тестирования в фрейм данных

## import train_test_split from sklearn
from sklearn.model_selection import train_test_split
train, test = train_test_split(df_features, test_size=0.2)

Взгляните как на обучающий набор, так и на набор для тестирования

## training set
train

Тогда у нас есть:

Посмотрим на набор для тестирования

## testing set
test

Тогда у нас есть:

Чтобы реализовать DictVectorizer, нам нужно преобразовать «userID» и «ID» (movieID) как категориальный идентификатор.

## convert userid and movieID as categorical identifier for DictVectorizer for both training set and testing set respectively
train[“ID”]=train[“ID”].apply(lambda x:”c”+str(x))
train[“userID”]=train[“userID”].apply(lambda x:”u”+str(x))
test[“ID”]=test[“ID”].apply(lambda x:”c”+str(x))
test[“userID”]=test[“userID”].apply(lambda x:”u”+str(x))

Тогда у нас есть:

Давайте посмотрим, как обучающий набор, так и набор тестов соответственно.

## check out training set
train

Тогда у нас есть:

Что касается набора для тестирования:

## check out testing set
test

Тогда у нас есть:

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

## only consider numbers of ID and numbers of movieID for feature vector
train_no_rating=train.drop([‘userRating’],axis=1)
test_no_rating=test.drop([‘userRating’],axis=1)
alldf=pd.concat([train_no_rating,test_no_rating])
data_num=alldf.shape
print(“alldf shape”,alldf.shape)

Тогда у нас есть:

alldf shape (19590, 2)

Давайте воспользуемся функциями векторизатора для создания столбцов на основе функций

##feature vectorizer to create columns based on the features
vector=DictVectorizer()
vector.fit_transform(alldf.to_dict(orient=’record’))
del alldf

Тогда у нас есть:

/usr/local/lib/python3.7/dist-packages/pandas/core/frame.py:1490: FutureWarning: Using short name for ‘orient’ is deprecated. Only the options: (‘dict’, list, ‘series’, ‘split’, ‘records’, ‘index’) will be used in a future version. Use one of the above to silence this warning. FutureWarning,

Далее нам нужно преобразовать векторы в массив

## transformation into array
x_train=vector.transform(train.to_dict(orient=’record’)).toarray()
x_test=vector.transform(test.to_dict(orient=’record’)).toarray()
print(“x_train shape”,x_train.shape)
print(“x_test shape”,x_test.shape)

Тогда у нас есть:

/usr/local/lib/python3.7/dist-packages/pandas/core/frame.py:1490: FutureWarning: Using short name for 'orient' is deprecated. Only the options: ('dict', list, 'series', 'split', 'records', 'index') will be used in a future version. Use one of the above to silence this warning.
  FutureWarning,
x_train shape (15672, 11818)
x_test shape (3918, 11818)

Нам нужно изменить форму как набора для тестирования, так и набора для обучения.

## reshape for both testing set and training set
y_train=train[‘userRating’].values.reshape(-1,1)
y_test=test[‘userRating’].values.reshape(-1,1)
print(“y_train shape”,y_train.shape)
print(“y_test shape”,y_test.shape)

Тогда у нас есть:

y_train shape (15672, 1) y_test shape (3918, 1)

Часть VI: Построение модели

Настроить параметры:

## set xparameter and yparameter for the x-axis of x_train shape and y-axis for y_train shape respectively
xparameter, yparameter =x_train.shape

Реализуем модель машины факторизации.

## Implement Fatorization machine model
class FM_model(pt.nn.Module):
def __init__(self, yparameter, k):
super(FM_model,self).__init__()
self.yparameter = yparameter #feature num
self.k = k #factor num
self.linear=pt.nn.Linear(self.yparameter, 1, bias=True) #linear part
self.v=pt.nn.Parameter(pt.rand(self.k,self.yparameter)) #interaction part
def fm_layer(self,x):
#linear part
linear_part=self.linear(pt.tensor(x).float())
#interaction part
inter_part1=pt.mm(pt.tensor(x).float(),self.v.t())
inter_part2=pt.mm(pt.pow(pt.tensor(x).float(),2),pt.pow(self.v,2).t())
pair_interactions=pt.sum(pt.sub(pt.pow(inter_part1,2),inter_part2),dim=1)
output=linear_part.transpose(1,0)+0.5*pair_interactions
return output
def forward(self, x):
output=self.fm_layer(x)
return output

Давайте установим число множителей как 10, можете смело изменять числа множителей, как и другие параметры, и поиграйте!

#numbers of factor
k=10
fm=FM_model(yparameter,k)
fm

Тогда у нас есть:

FM_model( (linear): Linear(in_features=11818, out_features=1, bias=True) )

Часть VI: Оптимизация модели

Внедрение оптимизатора SGD

##Using the SGD optimizaer as the optimization
##result in the training loss
optimizer=pt.optim.SGD(fm.parameters(),lr=0.01) #set learning rate as 0.01,free feel to change and play around
for i in range(20):
optimizer.zero_grad()
output=fm(x_train)
output=output.transpose(1,0)
#square difference
loss_func= pt.nn.MSELoss()
mse_loss=loss_func(output,pt.tensor(y_train).float())
l2_regularization=pt.tensor(0).float()
#l2 regularization
for param in fm.parameters():
l2_regularization+=pt.norm(param,2)
loss=mse_loss+l2_regularization
loss.backward()
optimizer.step() #update

Напечатаем результат

## print loss
print(loss)

Тогда имеем результат в проигрыше:

tensor(197.8685, grad_fn=<AddBackward0>)

Часть VIII: Оценка модели

Давайте оценим модель на основе оценки RMSE

## evaluation model based on the RMSE
def rmse(pred_rate,real_rate):
#implement root mean square error as evaluation
loss_func=pt.nn.MSELoss()
mse_loss=loss_func(pred_rate,pt.tensor(real_rate).float())
rmse_loss=pt.sqrt(mse_loss)
return rmse_loss

Напечатаем результат

#print result
pred=fm(x_test)
pred=pred.transpose(1,0)
rmse_loss=rmse(pred,y_test)
print(“test_loss”,rmse_loss)
print(y_test[0:5],” “,pred[0:5])

Тогда у нас есть:

test_loss tensor(2.3499, grad_fn=<SqrtBackward>) [[0.5] [0.6] [0.4] [0.7] [0.6]] tensor([[2.8638], [2.8216], [3.6933], [3.2920], [2.3876]], grad_fn=<SliceBackward>)

В целом, у нас есть оценка RMSE 2,3499 для идентификатора пользователя «118250».

Поздравляю! мы сделали! Если вам нравится этот контент, дайте мне знать в аплодисменты, и я добавлю больше подобных блогов в будущем. Не стесняйтесь обращаться ко мне через форум сообщества TigerGraph или чат разработчиков! - [TigerGraph (discord.com)]