電影數據集
The MovieLens Dataset
有許多數據集可用於推薦研究。其中,MovieLens數據集可能是最受歡迎的數據集之一。MovieLens是一個基於web的非商業電影推薦系統。創建於1997年,由明尼蘇達大學的一個研究實驗室GroupLens管理,目的是為了研究目的收集電影分級數據。MovieLens的數據對於包括個性化推薦和社會心理學在內的一些研究都是至關重要的。
1. Getting the Data
MovieLens數據集由GroupLens網站托管。有幾個版本可用。將使用MovieLens 100K數據集。此數據集包括10萬收視率,從1星到5星,從1682部電影的943名用戶。已經被清理,這樣每個用戶至少有20部電影。一些簡單的人口統計信息,如年齡、性別、用戶的類型和項目也可用。可以下載ml-100k.zip並提取u.data文件,其中包含所有10萬csv格式的評分。文件夾中還有許多其文件,每個文件的詳細說明可以在數據集的自述文件中找到。首先,讓導入運行本節實驗所需的包。
from d2l import mxnet as d2l
from mxnet import gluon, np
import os
import pandas as pd
然后,下載MovieLens 100k數據集並將交互作為DataFrame加載。
#@save
d2l.DATA_HUB['ml-100k'] = (
'http://files.grouplens.org/datasets/movielens/ml-100k.zip',
'cd4dcac4241c8a4ad7badc7ca635da8a69dddb83')
#@save
def read_data_ml100k():
data_dir = d2l.download_extract('ml-100k')
names = ['user_id', 'item_id', 'rating', 'timestamp']
data = pd.read_csv(os.path.join(data_dir, 'u.data'), '\t', names=names,
engine='python')
num_users = data.user_id.unique().shape[0]
num_items = data.item_id.unique().shape[0]
return data, num_users, num_items
2. Statistics of the Dataset
讓裝載數據並手動檢查前五個記錄。這是學習數據結構並驗證是否已正確加載的有效方法。
data, num_users, num_items = read_data_ml100k()
sparsity = 1 - len(data) / (num_users * num_items)
print('number of users: %d, number of items: %d.' % (num_users, num_items))
print('matrix sparsity: %f' % sparsity)
print(data.head(5))
number of users: 943, number of items: 1682.
matrix sparsity: 0.936953
user_id item_id rating timestamp
0 196 242 3 881250949
1 186 302 3 891717742
2 22 377 1 878887116
3 244 51 2 880606923
4 166 346 1 886397596
可以看到,每行由四列組成,包括“userid”1-943、“item id”1-1682、“rating”1-5和“timestamp”。可以構造一個尺寸的相互作用矩陣牛×米牛×米,其中nn和米米分別是用戶數和項目數。這個數據集只記錄現有的評級,所以也可以稱之為評級矩陣,如果這個矩陣的值代表確切的評級,將互換使用交互矩陣和評級矩陣。評級矩陣中的大多數值是未知的,因為用戶沒有對大多數電影進行評級。還展示了這個數據集的稀疏性。稀疏性定義為The sparsity is defined as 1-
number
of
nonzero
entries
/
(
number
of
users
*
number
of
items).。顯然,相互作用矩陣非常稀疏(即稀疏度=93.695%)。現實世界中的數據集可能會受到更大程度的稀疏性的影響,並且一直是構建推薦系統的長期挑戰。一個可行的解決方案是使用附加的附加信息,例如用戶/項目特性來緩解稀疏性。然后,繪制不同評級的計數分布圖。正如預期的那樣,這似乎是一個正態分布,大多數評級集中在3-4。
d2l.plt.hist(data['rating'], bins=5, ec='black')
d2l.plt.xlabel('Rating')
d2l.plt.ylabel('Count')
d2l.plt.title('Distribution of Ratings in MovieLens 100K')
d2l.plt.show()
3. Splitting the dataset
將數據集分成訓練集和測試集。以下函數提供兩種分割模式,包括隨機和序列感知。在隨機模式下,該函數在不考慮時間戳的情況下隨機分割100k個交互,默認使用90%的數據作為訓練樣本,其余10%作為測試樣本。在seq-aware模式中,將用戶最近評估的項目和用戶的歷史交互作為訓練集。用戶歷史交互根據時間戳從最舊到最新排序。此模式將在序列感知推薦部分中使用。
#@save
def split_data_ml100k(data, num_users, num_items,
split_mode='random', test_ratio=0.1):
"""Split the dataset in random mode or seq-aware mode."""
if split_mode == 'seq-aware':
train_items, test_items, train_list = {}, {}, []
for line in data.itertuples():
u, i, rating, time = line[1], line[2], line[3], line[4]
train_items.setdefault(u, []).append((u, i, rating, time))
if u not in test_items or test_items[u][-1] < time:
test_items[u] = (i, rating, time)
for u in range(1, num_users + 1):
train_list.extend(sorted(train_items[u], key=lambda k: k[3]))
test_data = [(key, *value) for key, value in test_items.items()]
train_data = [item for item in train_list if item not in test_data]
train_data = pd.DataFrame(train_data)
test_data = pd.DataFrame(test_data)
else:
mask = [True if x == 1 else False for x in np.random.uniform(
0, 1, (len(data))) < 1 - test_ratio]
neg_mask = [not x for x in mask]
train_data, test_data = data[mask], data[neg_mask]
return train_data, test_data
請注意,除了測試集之外,在實踐中使用驗證集是一個很好的實踐。然而,為了簡潔起見,省略了這一點。在這種情況下,測試集可以被視為保留驗證集。
4. Loading the data
數據集拆分后,為了方便起見,將訓練集和測試集轉換成列表和字典/矩陣。以下函數逐行讀取數據幀並從零開始枚舉用戶/項的索引。然后,該函數返回用戶、項目、評分和記錄交互的字典/矩陣的列表。可以將反饋的類型指定為顯式或隱式。
#@save
def load_data_ml100k(data, num_users, num_items, feedback='explicit'):
users, items, scores = [], [], []
inter = np.zeros((num_items, num_users)) if feedback == 'explicit' else {}
for line in data.itertuples():
user_index, item_index = int(line[1] - 1), int(line[2] - 1)
score = int(line[3]) if feedback == 'explicit' else 1
users.append(user_index)
items.append(item_index)
scores.append(score)
if feedback == 'implicit':
inter.setdefault(user_index, []).append(item_index)
else:
inter[item_index, user_index] = score
return users, items, scores, inter
之后,將上述步驟放在一起,並將在下一節中使用。結果用Dataset和DataLoader包裝。注意,訓練數據的最后一批DataLoader被設置為rollover模式(剩余的樣本被滾動到下一個epoch),並且順序被無序排列。
#@save
def split_and_load_ml100k(split_mode='seq-aware', feedback='explicit',
test_ratio=0.1, batch_size=256):
data, num_users, num_items = read_data_ml100k()
train_data, test_data = split_data_ml100k(
data, num_users, num_items, split_mode, test_ratio)
train_u, train_i, train_r, _ = load_data_ml100k(
train_data, num_users, num_items, feedback)
test_u, test_i, test_r, _ = load_data_ml100k(
test_data, num_users, num_items, feedback)
train_set = gluon.data.ArrayDataset(
np.array(train_u), np.array(train_i), np.array(train_r))
test_set = gluon.data.ArrayDataset(
np.array(test_u), np.array(test_i), np.array(test_r))
train_iter = gluon.data.DataLoader(
train_set, shuffle=True, last_batch='rollover',
batch_size=batch_size)
test_iter = gluon.data.DataLoader(
test_set, batch_size=batch_size)
return num_users, num_items, train_iter, test_iter
5. Summary
- MovieLens datasets are widely used for recommendation research. It is public available and free to use.
- We define functions to download and preprocess the MovieLens 100k dataset for further use in later sections.