CCF推薦系統(tǒng)項目代碼解讀!

實踐背景
賽題背景
隨著新型互聯(lián)網(wǎng)的發(fā)展,人類逐漸進入了信息爆炸時代。新型電商網(wǎng)絡(luò)面臨的問題也逐漸轉(zhuǎn)為如何讓用戶從海量的商品中挑選到自己想要的目標(biāo)。推薦系統(tǒng)正是在互聯(lián)網(wǎng)快速發(fā)展之后的產(chǎn)物。
為幫助電商系統(tǒng)識別用戶需求,為用戶提供其更加感興趣的信息,從而為用戶提供更好的服務(wù),需要依據(jù)真實的圖書閱讀數(shù)據(jù)集,利用機器學(xué)習(xí)的相關(guān)技術(shù),建立一個圖書推薦系統(tǒng)。用于為用戶推薦其可能進行閱讀的數(shù)據(jù),從而在產(chǎn)生商業(yè)價值的同時,提升用戶的閱讀體驗,幫助創(chuàng)建全民讀書的良好社會風(fēng)氣。
賽題任務(wù)
依據(jù)真實世界中的用戶-圖書交互記錄,利用機器學(xué)習(xí)相關(guān)技術(shù),建立一個精確穩(wěn)定的圖書推薦系統(tǒng),預(yù)測用戶可能會進行閱讀的書籍。
賽題數(shù)據(jù)
數(shù)據(jù)集來自公開數(shù)據(jù)集Goodbooks-10k,包含網(wǎng)站Goodreads中對10,000本書共約6,000,000條評分。為了預(yù)測用戶下一個可能的交互對象,數(shù)據(jù)集已經(jīng)處理為隱式交互數(shù)據(jù)集。該數(shù)據(jù)集廣泛的應(yīng)用于推薦系統(tǒng)中。
數(shù)據(jù)文件夾包含3個文件,依次為:
訓(xùn)練集: train.csv 訓(xùn)練數(shù)據(jù)集,為用戶-圖書交互記錄 測試集: test.csv 測試數(shù)據(jù)集,只有需要進行預(yù)測用戶ID 提交樣例: submission.csv 僅有兩個字段user_id/item_id
解題思路
使用深度學(xué)習(xí)模型構(gòu)建隱式推薦算法模型,并構(gòu)建負(fù)樣本,最終按照模型輸出的評分進行排序,做出最終的推薦。具體可以分為以下幾個步驟:
步驟1:讀取數(shù)據(jù),對用戶和圖書進行編碼; 步驟2:利用訓(xùn)練集構(gòu)建負(fù)樣本; 步驟3:使用Paddle構(gòu)建打分模型; 步驟4:對測試集數(shù)據(jù)進行預(yù)測;
步驟1:讀取數(shù)據(jù)集
首先我們使用pandas讀取數(shù)據(jù)集,并對數(shù)據(jù)的字段進行編碼。這里可以手動構(gòu)造編碼過程,也可以使用LabelEncoder來完成。
這一步驟的操作目的是將對用戶和圖書編碼為連續(xù)的數(shù)值,原始的取值并不是連續(xù)的,這樣可以減少后續(xù)模型所需要的空間。
步驟2:構(gòu)建負(fù)樣本
由于原始訓(xùn)練集中都是記錄的是用戶已有的圖書記錄,并不存在負(fù)樣本。而在預(yù)測階段我們需要預(yù)測用戶下一個圖書,此時的預(yù)測空間是用戶對所有圖書的關(guān)系。
這里構(gòu)建負(fù)樣本的操作非常粗暴,直接是選擇用戶在訓(xùn)練集中沒有圖書。這里可以先使用協(xié)同過濾的思路來構(gòu)建負(fù)樣本,即將負(fù)樣本是相似用戶都沒有記錄的圖書。
步驟3:Paddle搭建打分模型
這里使用Paddle構(gòu)建用戶與圖書的打分模型,借助Embedding層來完成具體的匹配過程。這里用最簡單的dot來完成匹配,沒有構(gòu)建復(fù)雜的模型。

步驟4:對測試集進行預(yù)測
首先將測試集數(shù)據(jù)轉(zhuǎn)為模型需要的格式,然后一行代碼完成預(yù)測即可,然后轉(zhuǎn)換為提交格式。
改進思路
由于現(xiàn)有的代碼寫的比較基礎(chǔ),所以有很多改進的步驟:
對模型精度進行改進,可以考慮構(gòu)建更加復(fù)雜的模型,并對訓(xùn)練集負(fù)樣本構(gòu)造過程進行改進。
對模型使用內(nèi)存,可以考慮使用Numpy代替Pandas的操作。
代碼實踐
讀取數(shù)據(jù)集
#?查看當(dāng)前掛載的數(shù)據(jù)集目錄,?該目錄下的變更重啟環(huán)境后會自動還原
#?View?dataset?directory.?
#?This?directory?will?be?recovered?automatically?after?resetting?environment.?
!unzip?/home/aistudio/data/data114712/train_dataset.zip
Archive:??/home/aistudio/data/data114712/train_dataset.zip
??inflating:?train_dataset.csv???????
!cp?/home/aistudio/data/data114712/test_dataset.csv?./
!head?train_dataset.csv
user_id,item_id
import?pandas?as?pd
import?numpy?as?np
import?paddle
import?paddle.nn?as?nn
from?paddle.io?import?Dataset
df?=?pd.read_csv('train_dataset.csv')
user_ids?=?df["user_id"].unique().tolist()
user2user_encoded?=?{x:?i?for?i,?x?in?enumerate(user_ids)}
userencoded2user?=?{i:?x?for?i,?x?in?enumerate(user_ids)}
book_ids?=?df["item_id"].unique().tolist()
book2book_encoded?=?{x:?i?for?i,?x?in?enumerate(book_ids)}
book_encoded2book?=?{i:?x?for?i,?x?in?enumerate(book_ids)}
df["user"]?=?df["user_id"].map(user2user_encoded)
df["movie"]?=?df["item_id"].map(book2book_encoded)
num_users?=?len(user2user_encoded)
num_books?=?len(book_encoded2book)
user_book_dict?=?df.iloc[:].groupby(['user'])['movie'].apply(list)
user_book_dict
user
構(gòu)造負(fù)樣本
neg_df?=?[]
book_set?=?set(list(book_encoded2book.keys()))
for?user_idx?in?user_book_dict.index:
????book_idx?=?book_set?-?set(list(user_book_dict.loc[user_idx]))
????book_idx?=?list(book_idx)
????neg_book_idx?=?np.random.choice(book_idx,?100)
????for?x?in?neg_book_idx:
????????neg_df.append([user_idx,?x])
neg_df?=?pd.DataFrame(neg_df,?columns=['user',?'movie'])
neg_df['label']?=?0
df['label']?=?1
train_df?=?pd.concat([df[['user',?'movie',?'label']],?
??????????????????????neg_df[['user',?'movie',?'label']]],?axis=0)
train_df?=?train_df.sample(frac=1)
del?df;
自定義數(shù)據(jù)集
#?自定義數(shù)據(jù)集
#?映射式(map-style)數(shù)據(jù)集需要繼承paddle.io.Dataset
class?SelfDefinedDataset(Dataset):
????def?__init__(self,?data_x,?data_y,?mode?=?'train'):
????????super(SelfDefinedDataset,?self).__init__()
????????self.data_x?=?data_x
????????self.data_y?=?data_y
????????self.mode?=?mode
????def?__getitem__(self,?idx):
????????if?self.mode?==?'predict':
????????????return?self.data_x[idx]
????????else:
????????????return?self.data_x[idx],?self.data_y[idx]
????
????def?__len__(self):
????????return?len(self.data_x)
from?sklearn.model_selection?import?train_test_split
x_train,?x_val,?y_train,?y_val?=?train_test_split(train_df[['user',?'movie']].values,?
????????????????????????????????????????train_df['label'].values.astype(np.float32).reshape(-1,?1))
traindataset?=?SelfDefinedDataset(x_train,?y_train)
for?data,?label?in?traindataset:
????print(data.shape,?label.shape)
????print(data,?label)
????break
train_loader?=?paddle.io.DataLoader(traindataset,?batch_size?=?1280*4,?shuffle?=?True)
for?batch_id,?data?in?enumerate(train_loader):
????x_data?=?data[0]
????y_data?=?data[1]
????
????print(x_data.shape)
??print(y_data.shape)
??break
??
val_dataset?=?SelfDefinedDataset(x_val,?y_val)
val_loader?=?paddle.io.DataLoader(val_dataset,?batch_size?=?1280*4,?shuffle?=?True)????????
for?batch_id,?data?in?enumerate(val_loader):
????x_data?=?data[0]
????y_data?=?data[1]
????
????print(x_data.shape)
??print(y_data.shape)
??break
定義模型
EMBEDDING_SIZE?=?32
class?RecommenderNet(nn.Layer):
????def?__init__(self,?num_users,?num_movies,?embedding_size):
????????super(RecommenderNet,?self).__init__()
????????self.num_users?=?num_users
????????self.num_movies?=?num_movies
????????self.embedding_size?=?embedding_size
????????weight_attr_user?=?paddle.ParamAttr(
????????????regularizer?=?paddle.regularizer.L2Decay(1e-6),
????????????initializer?=?nn.initializer.KaimingNormal()
????????????)
????????self.user_embedding?=?nn.Embedding(
????????????num_users,
????????????embedding_size,
????????????weight_attr=weight_attr_user
????????)
????????self.user_bias?=?nn.Embedding(num_users,?1)
????????
????????weight_attr_movie?=?paddle.ParamAttr(
????????regularizer?=?paddle.regularizer.L2Decay(1e-6),
????????initializer?=?nn.initializer.KaimingNormal()
????????)
????self.movie_embedding?=?nn.Embedding(
????????num_movies,
????????embedding_size,
????????weight_attr=weight_attr_movie
????)
????self.movie_bias?=?nn.Embedding(num_movies,?1)
????
??def?forward(self,?inputs):
??????user_vector?=?self.user_embedding(inputs[:,?0])
??????user_bias?=?self.user_bias(inputs[:,?0])
??????movie_vector?=?self.movie_embedding(inputs[:,?1])
??????movie_bias?=?self.movie_bias(inputs[:,?1])
??????dot_user_movie?=?paddle.dot(user_vector,?movie_vector)
??????x?=?dot_user_movie?+?user_bias?+?movie_bias
??????x?=?nn.functional.sigmoid(x)
??????return?x
model?=?RecommenderNet(num_users,?num_books,?EMBEDDING_SIZE)
model?=?paddle.Model(model)
optimizer?=?paddle.optimizer.Adam(parameters=model.parameters(),?learning_rate=0.003)
loss?=?nn.BCELoss()
metric?=?paddle.metric.Precision()
##?設(shè)置visualdl路徑
log_dir?=?'./visualdl'
callback?=?paddle.callbacks.VisualDL(log_dir=log_dir)
model.prepare(optimizer,?loss,?metric)
model.fit(train_loader,?val_loader,?epochs=5,?save_dir='./checkpoints',?verbose=1,?callbacks=callback)
預(yù)測測試集
test_df?=?[]
with?open('sub.csv',?'w')?as?up:
????up.write('user_id,item_id\n')
????
book_set?=?set(list(book_encoded2book.keys()))
for?idx?in?range(int(len(user_book_dict)/1000)?+1):
????test_user_idx?=?[]
????test_book_idx?=?[]
????for?user_idx?in?user_book_dict.index[idx*1000:(idx+1)*1000]:
????????book_idx?=?book_set?-?set(list(user_book_dict.loc[user_idx]))
????????book_idx?=?list(book_idx)
????????test_user_idx?+=?[user_idx]?*?len(book_idx)
????????test_book_idx?+=??book_idx
????test_data?=?np.array([test_user_idx,?test_book_idx]).T
????test_dataset?=?SelfDefinedDataset(test_data,?data_y=None,?mode='predict')
????test_loader?=?paddle.io.DataLoader(test_dataset,?batch_size=1280,?shuffle?=?False)????????
????test_predict?=?model.predict(test_loader,?batch_size=1024)
????test_predict?=?np.concatenate(test_predict[0],?0)
????test_data?=?pd.DataFrame(test_data,?columns=['user',?'book'])
????test_data['label']?=?test_predict
????for?gp?in?test_data.groupby(['user']):
????????with?open('sub.csv',?'a')?as?up:
????????????u?=?gp[0]
????????????b?=?gp[1]['book'].iloc[gp[1]['label'].argmax()]
????????????up.write(f'{userencoded2user[u]},?{book_encoded2book[b]}\n')
????del?test_data,?test_dataset,?test_loader
????????
