本文档说明RecStudio中支持的几种数据集类型的数据格式和适用场景。
TripletDataset
1. 说明
TripletDataset 的名字取自于三元组(用户,物品,交互),它是 RecStudio 中最基础的、应用最广泛的数据集类型。
2. 解析前后的数据格式
以dataset_demo中的ml-100k数据集举例,展示TripletDataset类加载数据集文件前后,数据的组织形式:
解析前:
解析前各个文件的原始内容为(只列出前五行):
user_id age gender occupation zip_code
1 24 M technician 85711
2 53 F other 94043
4 24 M technician 43537
5 33 F other 15213
...
item_id movie_title release_year class
1 Toy Story 1995 Animation Children's Comedy
2 GoldenEye 1995 Action Adventure Thriller
3 Four Rooms
4 Get Shorty 1995 Action Comedy Drama
...
user_id item_id rating timestamp
196 242 3 881250949
186 302 3 891717742
22 377 1 878887116
244 51 2 880606923
...
ml-100k对应的配置文件为:
url: "recstudio:dataset_demo/ml-100k"
user_id_field: &u user_id:token
item_id_field: &i item_id:token
rating_field: &r rating:float
time_field: &t timestamp:float
time_format: ~
inter_feat_name: ml-100k.inter
inter_feat_field: [*u, *i, *r, *t]
inter_feat_header: 0
user_feat_name: [ml-100k.user]
user_feat_field: [[*u, age:token, gender:token, occupation:token, zip_code:token]]
user_feat_header: 0
item_feat_name: [ml-100k.item]
item_feat_field: [[*i, movie_title:token_seq:" ", release_year:token, class:token_seq:" "]]
item_feat_header: 0
field_separator: "\t"
min_user_inter: 0
min_item_inter: 0
field_max_len: ~
rating_threshold: ~
ranker_rating_threshold: 3
drop_low_rating: ~
max_seq_len: 20
save_cache: False # whether to save processed dataset to cache.
解析后:
下面以最终传入模型的train_data(TripletDataset类的实例)为例,展示三元组(用户,物品,交互)信息在TripletDataset类中的组织形式。 为了方便存储和运算,解析后的user和item属性值都进行了映射,记录映射信息的字典存放于train_data.field2token2idx:
{'user_id': {'[PAD]': 0, '196': 1, '186': 2, '22': 3, '244': 4, '166': 5, '298': 6, '115': 7, '253': 8, ...},
'item_id': {'[PAD]': 0, '242': 1, '302': 2, '377': 3, '51': 4, '346': 5, '474': 6, '265': 7, '465': 8, ...},
'age': {'[PAD]': 0, '24': 1, '53': 2, '33': 3, '42': 4, '57': 5, '36': 6, '29': 7, '39': 8, ...},
'gender': {'[PAD]': 0, 'M': 1, 'F': 2},
'occupation': {'[PAD]': 0, 'technician': 1, 'other': 2, 'executive': 3, 'administrator': 4, 'student': 5, 'lawyer': 6, 'educator': 7, 'scientist': 8, ...},
'zip_code': {'[PAD]': 0, '85711': 1, '94043': 2, '43537': 3, '15213': 4, '98101': 5, '91344': 6, '05201': 7, '01002': 8, ...},
'movie_title': {'[PAD]': 0, 'Toy': 1, 'Story': 2, 'GoldenEye': 3, 'Four': 4, 'Rooms': 5, 'Get': 6, 'Shorty': 7, 'Copycat': 8, ...},
'release_year': {'1995': 1, '[PAD]': 0, '1994': 2, '1996': 3, '1976': 4, '1967': 5, '1977': 6, '1993': 7, '1965': 8, ...},
'class': {'[PAD]': 0, 'Animation': 1, "Children's": 2, 'Comedy': 3, 'Action': 4, 'Adventure': 5, 'Thriller': 6, 'Drama': 7, 'Crime': 8, ...}}
映射后,三元组信息的存放格式如下。
用户特征以
{'user_id': tensor([ 0, 1, ...942, 943]),
'age': tensor([ 0, 12, 8, ..., 14, 24]),
'gender': tensor([0, 1, 2, 1, ... 2, 2, 1]),
'occupation': tensor([ 0, 13, 3, ..., 14, 5]),
'zip_code': tensor([ 0, 35, 17...777, 792])}
物品特征以
{'item_id': tensor([ 0, 1, ...81, 1682]),
'movie_title': tensor([[ 0, 0,...0, 0]]),
'release_year': tensor([ 0, 3, 39, ..., 2, 1]),
'class': tensor([[ 0, 0, 0,... 0, 0]])}
交互特征以
{'user_id': tensor([ 1, 1, ...943, 943]),
'item_id': tensor([551, 650, 52... 13, 181]),
'rating': tensor([5., 4., 4., ..., 2., 5.]),
'timestamp': tensor([8.8125e+08, ...7505e+08])}
3. 数据集预处理流程
TripletDataset类并不是简单地加载并复制原始数据集文件。在组织好所有数据并交给模型之前,TripletDataset类对读入的原始数据进行了一系列的预处理。 预处理的具体过程不是固定的,具体的处理策略要根据config的设置来执行(修改config的方式视TripletDataset类的使用形式而定,在“快速开始”、“模块化模型设计”和“高级使用”中都有相应的介绍,这里不展开叙述)。本例使用的数据集同上,TripletDataset类的config为:
{'url': 'recstudio:dataset_demo/ml-100k',
'user_id_field': 'user_id:token',
'item_id_field': 'item_id:token',
'rating_field': 'rating:float',
'time_field': 'timestamp:float',
'time_format': None,
'inter_feat_name': 'ml-100k.inter',
'inter_feat_field': ['user_id:token', 'item_id:token', 'rating:float', 'timestamp:float'],
'inter_feat_header': 0,
'user_feat_name': ['ml-100k.user'],
'user_feat_field': [['user_id:token', 'age:token', 'gender:token', 'occupation:token', 'zip_code:token']],
'user_feat_header': 0,
'item_feat_name': ['ml-100k.item'],
'item_feat_field': [['item_id:token', 'movie_title:token_seq:" "', 'release_year:token', 'class:token_seq:" "']],
'item_feat_header': 0,
'field_separator': '\t',
'min_user_inter': 0,
'min_item_inter': 0,
'field_max_len': None,
'rating_threshold': None,
'ranker_rating_threshold': 3,
'drop_low_rating': None,
'max_seq_len': 20,
'save_cache': False}
下面介绍预处理的流程。
- 读入三元组信息:
- TripletDataset读入交互数据,并剔除特征不完整的样本,保存在self.inter_feat中。 如果原数据集中没有rating_field(评分特征),那么添加一列rating,所有值为1(隐式反馈)。
- TripletDataset分别读入用户、物品特征,然后填充空值,分别保存在self.user_feat和self.item_feat中。 float型特征会用该特征均值填充,token型特征会用'[PAD]'(如果该特征已经映射过则使用0)填充, float_seq和token_seq分别用对应数据类型的空numpy array来填充。
- 过滤三元组信息:
- 根据self.config['rating_threshold'],对评分进行处理。(具体的过滤方式上次讨论说要修改,还要加一个变量,等改完再写)
- 对于self.inter_feat(交互数据)中[self.fuid, self.fiid]重复的项(即同一用户对同一商品的交互),只保留第一项,去掉其余项。(该功能是否重复?好像出现了两次,在init和build中)
- 剔除交互很少的用户和物品。
- 从三元组中剔除交互物品数少于self.config['min_user_inter']的用户
- 再剔除交互用户数少于self.config['min_item_inter']的物品。
- 由于第 ii 步可能减少一些用户的交互数,导致又出现一些新的用户,其交互物品数少于阈值,所以循环执行前两步直到所有用户和物品的交互数都不低于阈值。
- (源代码这里有个备注,only delete users/items in inter_feat, users/items in user/item_feat should also be deleted. 但是我看后面好像已经有相应代码了,该备注是不是可以删去?)
- 把tokens映射到从0开始的index上(实际上是从1开始,因为'[PAD]'会被映射到0)。映射字典会保存在self.field2token2idx中,该字典的形式上文已展示过。
- 一些具体特征的处理:
- 对self.inter_feat的时间戳(如果有)的数据类型检验。特别地,如果为str型,检验其是否与self.config['time_format']匹配,并进行相应的解析;如果不匹配则报错。
- 对self.user_feat和self.item_feat,分别根据user_id和item_id从小到大的顺序进行重排。
- 根据模型的split_mode、ratio_or_num和shuffle参数进一步处理self.inter_feat(交互数据),并进行训练集/验证集/测试集划分:
- ratio_or_num用来划分训练集/验证集/测试集,为list型(其中的三个元素均为float型,分别代表训练集、验证集、测试集的比例)。(这里留一法应该是只能给seq dataset用,但是别的数据集用也不会报错,上次跟师兄讨论说之后可以绑定一下)
- 如果split_mode为'user_entry',那么先按用户分组,然后每个用户的交互信息(self.inter_feat)按ratio_or_num进行划分。如果shuffle为True,那么每个用户的交互信息在划分前会先打乱顺序。
- 如果split_mode为'user',那么按用户划分,即所有用户按ratio_or_num划分成三组,作为训练集/验证集/测试集。
- 如果split_mode为'entry',那么所有用户的交互信息(self.inter_feat)混在一起,按ratio_or_num进行划分。如果shuffle为True,那么所有的的交互信息在划分前会先打乱顺序。
4. 样本包含的特征
- 对于FM类模型来说,train和validation/test阶段的数据形式是一样的,每个数据样本都对应一个用户、一个物品、一个评分。
- 对于其他类模型来说,train阶段的每个数据样本对应一个用户、一个物品、一个评分,而validation/test阶段的每个数据样本对应一个用户、多个物品和对应的多个评分。
UserDataset
1. 说明
UserDataset 的名称源自于样本的划分方式,该数据集是按用户来划分样本的,每个样本包括了一个用户及其所有交互历史。
2. 解析前后的数据格式
以dataset_demo中的ml-100k数据集举例,展示UserDataset类加载数据集文件前后,数据的组织形式:
解析前:
解析前各个文件的原始内容为(只列出前五行):
user_id age gender occupation zip_code
1 24 M technician 85711
2 53 F other 94043
4 24 M technician 43537
5 33 F other 15213
...
item_id movie_title release_year class
1 Toy Story 1995 Animation Children's Comedy
2 GoldenEye 1995 Action Adventure Thriller
3 Four Rooms
4 Get Shorty 1995 Action Comedy Drama
...
user_id item_id rating timestamp
196 242 3 881250949
186 302 3 891717742
22 377 1 878887116
244 51 2 880606923
...
ml-100k对应的配置文件为:
url: "recstudio:dataset_demo/ml-100k"
user_id_field: &u user_id:token
item_id_field: &i item_id:token
rating_field: &r rating:float
time_field: &t timestamp:float
time_format: ~
inter_feat_name: ml-100k.inter
inter_feat_field: [*u, *i, *r, *t]
inter_feat_header: 0
user_feat_name: [ml-100k.user]
user_feat_field: [[*u, age:token, gender:token, occupation:token, zip_code:token]]
user_feat_header: 0
item_feat_name: [ml-100k.item]
item_feat_field: [[*i, movie_title:token_seq:" ", release_year:token, class:token_seq:" "]]
item_feat_header: 0
field_separator: "\t"
min_user_inter: 0
min_item_inter: 0
field_max_len: ~
rating_threshold: ~
ranker_rating_threshold: 3
drop_low_rating: ~
max_seq_len: 20
save_cache: False # whether to save processed dataset to cache.
解析后:
下面以最终传入模型的train_data(UserDataset类的实例)为例,展示样本信息在UserDataset类中的组织形式。 为了方便存储和运算,解析后的user和item属性值都进行了映射,记录映射信息的字典存放于train_data.field2token2idx:
{'user_id': {'[PAD]': 0, '196': 1, '186': 2, '22': 3, '244': 4, '166': 5, '298': 6, '115': 7, '253': 8, ...},
'item_id': {'[PAD]': 0, '242': 1, '302': 2, '377': 3, '51': 4, '346': 5, '474': 6, '265': 7, '465': 8, ...},
'age': {'[PAD]': 0, '24': 1, '53': 2, '33': 3, '42': 4, '57': 5, '36': 6, '29': 7, '39': 8, ...},
'gender': {'[PAD]': 0, 'M': 1, 'F': 2},
'occupation': {'[PAD]': 0, 'technician': 1, 'other': 2, 'executive': 3, 'administrator': 4, 'student': 5, 'lawyer': 6, 'educator': 7, 'scientist': 8, ...},
'zip_code': {'[PAD]': 0, '85711': 1, '94043': 2, '43537': 3, '15213': 4, '98101': 5, '91344': 6, '05201': 7, '01002': 8, ...},
'movie_title': {'[PAD]': 0, 'Toy': 1, 'Story': 2, 'GoldenEye': 3, 'Four': 4, 'Rooms': 5, 'Get': 6, 'Shorty': 7, 'Copycat': 8, ...},
'release_year': {'1995': 1, '[PAD]': 0, '1994': 2, '1996': 3, '1976': 4, '1967': 5, '1977': 6, '1993': 7, '1965': 8, ...},
'class': {'[PAD]': 0, 'Animation': 1, "Children's": 2, 'Comedy': 3, 'Action': 4, 'Adventure': 5, 'Thriller': 6, 'Drama': 7, 'Crime': 8, ...}}
映射后,样本信息的存放格式如下。
用户特征以
{'user_id': tensor([ 0, 1, ...942, 943]),
'age': tensor([ 0, 12, 8, ..., 14, 24]),
'gender': tensor([0, 1, 2, 1, ... 2, 2, 1]),
'occupation': tensor([ 0, 13, 3, ..., 14, 5]),
'zip_code': tensor([ 0, 35, 17...777, 792])}
物品特征以
{'item_id': tensor([ 0, 1, ...81, 1682]),
'movie_title': tensor([[ 0, 0,...0, 0]]),
'release_year': tensor([ 0, 3, 39, ..., 2, 1]),
'class': tensor([[ 0, 0, 0,... 0, 0]])}
交互特征以
{'user_id': tensor([ 1, 1, ...943, 943]),
'item_id': tensor([551, 650, 52... 13, 181]),
'rating': tensor([5., 4., 4., ..., 2., 5.]),
'timestamp': tensor([8.8125e+08, ...7505e+08])}
3. 数据集预处理流程
UserDataset类并不是简单地加载并复制原始数据集文件。在组织好所有数据并交给模型之前,UserDataset类对读入的原始数据进行了一系列的预处理。 预处理的具体过程不是固定的,具体的处理策略要根据config的设置来执行(修改config的方式视UserDataset类的使用形式而定,在“快速开始”、“模块化模型设计”和“高级使用”中都有相应的介绍,这里不展开叙述)。本例使用的数据集同上,UserDataset类的config为:
{'url': 'recstudio:dataset_demo/ml-100k',
'user_id_field': 'user_id:token',
'item_id_field': 'item_id:token',
'rating_field': 'rating:float',
'time_field': 'timestamp:float',
'time_format': None,
'inter_feat_name': 'ml-100k.inter',
'inter_feat_field': ['user_id:token', 'item_id:token', 'rating:float', 'timestamp:float'],
'inter_feat_header': 0,
'user_feat_name': ['ml-100k.user'],
'user_feat_field': [['user_id:token', 'age:token', 'gender:token', 'occupation:token', 'zip_code:token']],
'user_feat_header': 0,
'item_feat_name': ['ml-100k.item'],
'item_feat_field': [['item_id:token', 'movie_title:token_seq:" "', 'release_year:token', 'class:token_seq:" "']],
'item_feat_header': 0,
'field_separator': '\t',
'min_user_inter': 0,
'min_item_inter': 0,
'field_max_len': None,
'rating_threshold': None,
'ranker_rating_threshold': 3,
'drop_low_rating': None,
'max_seq_len': 20,
'save_cache': False}
下面介绍预处理的流程。
- 读入样本信息:
- UserDataset读入交互数据,并剔除特征不完整的样本,保存在self.inter_feat中。 如果原数据集中没有rating_field(评分特征),那么添加一列rating,所有值为1(隐式反馈)。
- UserDataset分别读入用户、物品特征,然后填充空值,分别保存在self.user_feat和self.item_feat中。 float型特征会用该特征均值填充,token型特征会用'[PAD]'(如果该特征已经映射过则使用0)填充, float_seq和token_seq分别用对应数据类型的空numpy array来填充。
- 过滤样本信息:
- 根据self.config['rating_threshold'],对评分进行处理。(具体的过滤方式上次讨论说要修改,还要加一个变量,等改完再写)
- 对于self.inter_feat(交互数据)中[self.fuid, self.fiid]重复的项(即同一用户对同一商品的交互),只保留第一项,去掉其余项。
- 剔除交互很少的用户和物品。
- 从样本中剔除交互物品数少于self.config['min_user_inter']的用户
- 再剔除交互用户数少于self.config['min_item_inter']的物品。
- 由于第 ii 步可能减少一些用户的交互数,导致又出现一些新的用户,其交互物品数少于阈值,所以循环执行前两步直到所有用户和物品的交互数都不低于阈值。
- 把tokens映射到从0开始的index上(实际上是从1开始,因为'[PAD]'会被映射到0)。映射字典会保存在self.field2token2idx中,该字典的形式上文已展示过。
- 一些具体特征的处理:
- 对self.inter_feat的时间戳(如果有)的数据类型检验。特别地,如果为str型,检验其是否与self.config['time_format']匹配,并进行相应的解析;如果不匹配则报错。
- 对self.user_feat和self.item_feat,分别根据user_id和item_id从小到大的顺序进行重排。
- 根据模型的split_mode、ratio_or_num和shuffle参数进一步处理self.inter_feat(交互数据),并进行训练集/验证集/测试集划分:
- ratio_or_num用来划分训练集/验证集/测试集,为list型(其中的三个元素均为float型,分别代表训练集、验证集、测试集的比例)。
- 如果split_mode为'user_entry',那么先按用户分组,然后每个用户的交互信息(self.inter_feat)按ratio_or_num进行划分。如果shuffle为True,那么每个用户的交互信息在划分前会先打乱顺序。
- 如果split_mode为'user',那么按用户划分,即所有用户按ratio_or_num划分成三组,作为训练集/验证集/测试集。
- 如果split_mode为'entry',那么所有用户的交互信息(self.inter_feat)混在一起,按ratio_or_num进行划分。如果shuffle为True,那么所有的的交互信息在划分前会先打乱顺序。
4. 样本包含的特征
- 训练阶段的每个数据样本对应一个用户、一个正例物品及其评分、所有历史交互物品及其评分,而验证/测试阶段的每个数据样本对应一个用户、多个正例物品及其对应的多个评分、该用户的所有历史交互物品及其评分。
SeqDataset
1. 说明
SeqDataset 的名称源自于样本的序列特征,该数据集的每个样本包括了一个用户和他的某段交互序列。
2. 解析前后的数据格式
以dataset_demo中的ml-100k数据集举例,展示SeqDataset类加载数据集文件前后,数据的组织形式:
解析前:
解析前各个文件的原始内容为(只列出前五行):
user_id age gender occupation zip_code
1 24 M technician 85711
2 53 F other 94043
4 24 M technician 43537
5 33 F other 15213
...
item_id movie_title release_year class
1 Toy Story 1995 Animation Children's Comedy
2 GoldenEye 1995 Action Adventure Thriller
3 Four Rooms
4 Get Shorty 1995 Action Comedy Drama
...
user_id item_id rating timestamp
196 242 3 881250949
186 302 3 891717742
22 377 1 878887116
244 51 2 880606923
...
ml-100k对应的配置文件为:
url: "recstudio:dataset_demo/ml-100k"
user_id_field: &u user_id:token
item_id_field: &i item_id:token
rating_field: &r rating:float
time_field: &t timestamp:float
time_format: ~
inter_feat_name: ml-100k.inter
inter_feat_field: [*u, *i, *r, *t]
inter_feat_header: 0
user_feat_name: [ml-100k.user]
user_feat_field: [[*u, age:token, gender:token, occupation:token, zip_code:token]]
user_feat_header: 0
item_feat_name: [ml-100k.item]
item_feat_field: [[*i, movie_title:token_seq:" ", release_year:token, class:token_seq:" "]]
item_feat_header: 0
field_separator: "\t"
min_user_inter: 0
min_item_inter: 0
field_max_len: ~
rating_threshold: ~
ranker_rating_threshold: 3
drop_low_rating: ~
max_seq_len: 20
save_cache: False # whether to save processed dataset to cache.
解析后:
下面以最终传入模型的train_data(SeqDataset类的实例)为例,展示样本信息在SeqDataset类中的组织形式。 为了方便存储和运算,解析后的user和item属性值都进行了映射,记录映射信息的字典存放于train_data.field2token2idx:
{'user_id': {'[PAD]': 0, '196': 1, '186': 2, '22': 3, '244': 4, '166': 5, '298': 6, '115': 7, '253': 8, ...},
'item_id': {'[PAD]': 0, '242': 1, '302': 2, '377': 3, '51': 4, '346': 5, '474': 6, '265': 7, '465': 8, ...},
'age': {'[PAD]': 0, '24': 1, '53': 2, '33': 3, '42': 4, '57': 5, '36': 6, '29': 7, '39': 8, ...},
'gender': {'[PAD]': 0, 'M': 1, 'F': 2},
'occupation': {'[PAD]': 0, 'technician': 1, 'other': 2, 'executive': 3, 'administrator': 4, 'student': 5, 'lawyer': 6, 'educator': 7, 'scientist': 8, ...},
'zip_code': {'[PAD]': 0, '85711': 1, '94043': 2, '43537': 3, '15213': 4, '98101': 5, '91344': 6, '05201': 7, '01002': 8, ...},
'movie_title': {'[PAD]': 0, 'Toy': 1, 'Story': 2, 'GoldenEye': 3, 'Four': 4, 'Rooms': 5, 'Get': 6, 'Shorty': 7, 'Copycat': 8, ...},
'release_year': {'1995': 1, '[PAD]': 0, '1994': 2, '1996': 3, '1976': 4, '1967': 5, '1977': 6, '1993': 7, '1965': 8, ...},
'class': {'[PAD]': 0, 'Animation': 1, "Children's": 2, 'Comedy': 3, 'Action': 4, 'Adventure': 5, 'Thriller': 6, 'Drama': 7, 'Crime': 8, ...}}
映射后,样本信息的存放格式如下。
用户特征以
{'user_id': tensor([ 0, 1, ...942, 943]),
'age': tensor([ 0, 12, 8, ..., 14, 24]),
'gender': tensor([0, 1, 2, 1, ... 2, 2, 1]),
'occupation': tensor([ 0, 13, 3, ..., 14, 5]),
'zip_code': tensor([ 0, 35, 17...777, 792])}
物品特征以
{'item_id': tensor([ 0, 1, ...81, 1682]),
'movie_title': tensor([[ 0, 0,...0, 0]]),
'release_year': tensor([ 0, 3, 39, ..., 2, 1]),
'class': tensor([[ 0, 0, 0,... 0, 0]])}
交互特征以
{'user_id': tensor([ 1, 1, ...943, 943]),
'item_id': tensor([551, 650, 52... 13, 181]),
'rating': tensor([5., 4., 4., ..., 2., 5.]),
'timestamp': tensor([8.8125e+08, ...7505e+08])}
3. 数据集预处理流程
SeqDataset类并不是简单地加载并复制原始数据集文件。在组织好所有数据并交给模型之前,SeqDataset类对读入的原始数据进行了一系列的预处理。 预处理的具体过程不是固定的,具体的处理策略要根据config的设置来执行(修改config的方式视SeqDataset类的使用形式而定,在“快速开始”、“模块化模型设计”和“高级使用”中都有相应的介绍,这里不展开叙述)。本例使用的数据集同上,SeqDataset类的config为:
{'url': 'recstudio:dataset_demo/ml-100k',
'user_id_field': 'user_id:token',
'item_id_field': 'item_id:token',
'rating_field': 'rating:float',
'time_field': 'timestamp:float',
'time_format': None,
'inter_feat_name': 'ml-100k.inter',
'inter_feat_field': ['user_id:token', 'item_id:token', 'rating:float', 'timestamp:float'],
'inter_feat_header': 0,
'user_feat_name': ['ml-100k.user'],
'user_feat_field': [['user_id:token', 'age:token', 'gender:token', 'occupation:token', 'zip_code:token']],
'user_feat_header': 0,
'item_feat_name': ['ml-100k.item'],
'item_feat_field': [['item_id:token', 'movie_title:token_seq:" "', 'release_year:token', 'class:token_seq:" "']],
'item_feat_header': 0,
'field_separator': '\t',
'min_user_inter': 0,
'min_item_inter': 0,
'field_max_len': None,
'rating_threshold': None,
'ranker_rating_threshold': 3,
'drop_low_rating': None,
'max_seq_len': 20,
'save_cache': False}
下面介绍预处理的流程。
- 读入样本信息:
- SeqDataset读入交互数据,并剔除特征不完整的样本,保存在self.inter_feat中。 如果原数据集中没有rating_field(评分特征),那么添加一列rating,所有值为1(隐式反馈)。
- SeqDataset分别读入用户、物品特征,然后填充空值,分别保存在self.user_feat和self.item_feat中。 float型特征会用该特征均值填充,token型特征会用'[PAD]'(如果该特征已经映射过则使用0)填充, float_seq和token_seq分别用对应数据类型的空numpy array来填充。
- 过滤样本信息:
- 根据self.config['rating_threshold'],对评分进行处理。
- 对于self.inter_feat(交互数据)中[self.fuid, self.fiid]重复的项(即同一用户对同一商品的交互),只保留第一项,去掉其余项。
- 剔除交互很少的用户和物品。
- 从样本中剔除交互物品数少于self.config['min_user_inter']的用户
- 再剔除交互用户数少于self.config['min_item_inter']的物品。
- 由于第 ii 步可能减少一些用户的交互数,导致又出现一些新的用户,其交互物品数少于阈值,所以循环执行前两步直到所有用户和物品的交互数都不低于阈值。
- 把tokens映射到从0开始的index上(实际上是从1开始,因为'[PAD]'会被映射到0)。映射字典会保存在self.field2token2idx中,该字典的形式上文已展示过。
- 一些具体特征的处理:
- 对self.inter_feat的时间戳(如果有)的数据类型检验。特别地,如果为str型,检验其是否与self.config['time_format']匹配,并进行相应的解析;如果不匹配则报错。
- 对self.user_feat和self.item_feat,分别根据user_id和item_id从小到大的顺序进行重排。
- 根据模型的split_mode和shuffle参数进一步处理self.inter_feat(交互数据),并进行训练集/验证集/测试集划分:
- 该数据集默认使用留一法,即每个用户的最后一条交互数据放入测试集,倒数第二条(如果一共只有两条则还用倒数第一条)交互数据放入验证集,其余都放入训练集。
- 如果split_mode为'user_entry',那么先按用户分组,然后每个用户的交互信息(self.inter_feat)按ratio_or_num进行划分。如果shuffle为True,那么每个用户的交互信息在划分前会先打乱顺序。
- 如果split_mode为'user',那么按用户划分,即所有用户按ratio_or_num划分成三组,作为训练集/验证集/测试集。
- 如果split_mode为'entry',那么所有用户的交互信息(self.inter_feat)混在一起,按ratio_or_num进行划分。如果shuffle为True,那么所有的的交互信息在划分前会先打乱顺序。
4. 样本包含的特征
- 该数据集默认使用留一法,那么train和validation/test阶段的数据形式是一样的,每个数据样本对应一个用户、一个正例物品及其评分、一段历史交互物品序列及其评分、该序列长度。
ALSDataset
1. 说明
ALSDataset 的名称是Alternating Least Squares(即最小交替二乘)的缩写。
2. 解析前后的数据格式
以dataset_demo中的ml-100k数据集举例,展示ALSDataset类加载数据集文件前后,数据的组织形式:
解析前:
解析前各个文件的原始内容为(只列出前五行):
user_id age gender occupation zip_code
1 24 M technician 85711
2 53 F other 94043
4 24 M technician 43537
5 33 F other 15213
...
item_id movie_title release_year class
1 Toy Story 1995 Animation Children's Comedy
2 GoldenEye 1995 Action Adventure Thriller
3 Four Rooms
4 Get Shorty 1995 Action Comedy Drama
...
user_id item_id rating timestamp
196 242 3 881250949
186 302 3 891717742
22 377 1 878887116
244 51 2 880606923
...
ml-100k对应的配置文件为:
url: "recstudio:dataset_demo/ml-100k"
user_id_field: &u user_id:token
item_id_field: &i item_id:token
rating_field: &r rating:float
time_field: &t timestamp:float
time_format: ~
inter_feat_name: ml-100k.inter
inter_feat_field: [*u, *i, *r, *t]
inter_feat_header: 0
user_feat_name: [ml-100k.user]
user_feat_field: [[*u, age:token, gender:token, occupation:token, zip_code:token]]
user_feat_header: 0
item_feat_name: [ml-100k.item]
item_feat_field: [[*i, movie_title:token_seq:" ", release_year:token, class:token_seq:" "]]
item_feat_header: 0
field_separator: "\t"
min_user_inter: 0
min_item_inter: 0
field_max_len: ~
rating_threshold: ~
ranker_rating_threshold: 3
drop_low_rating: ~
max_seq_len: 20
save_cache: False # whether to save processed dataset to cache.
解析后:
下面以最终传入模型的train_data(ALSDataset类的实例)为例,展示样本信息在UserDataset类中的组织形式。 为了方便存储和运算,解析后的user和item属性值都进行了映射,记录映射信息的字典存放于train_data.field2token2idx:
{'user_id': {'[PAD]': 0, '196': 1, '186': 2, '22': 3, '244': 4, '166': 5, '298': 6, '115': 7, '253': 8, ...},
'item_id': {'[PAD]': 0, '242': 1, '302': 2, '377': 3, '51': 4, '346': 5, '474': 6, '265': 7, '465': 8, ...},
'age': {'[PAD]': 0, '24': 1, '53': 2, '33': 3, '42': 4, '57': 5, '36': 6, '29': 7, '39': 8, ...},
'gender': {'[PAD]': 0, 'M': 1, 'F': 2},
'occupation': {'[PAD]': 0, 'technician': 1, 'other': 2, 'executive': 3, 'administrator': 4, 'student': 5, 'lawyer': 6, 'educator': 7, 'scientist': 8, ...},
'zip_code': {'[PAD]': 0, '85711': 1, '94043': 2, '43537': 3, '15213': 4, '98101': 5, '91344': 6, '05201': 7, '01002': 8, ...},
'movie_title': {'[PAD]': 0, 'Toy': 1, 'Story': 2, 'GoldenEye': 3, 'Four': 4, 'Rooms': 5, 'Get': 6, 'Shorty': 7, 'Copycat': 8, ...},
'release_year': {'1995': 1, '[PAD]': 0, '1994': 2, '1996': 3, '1976': 4, '1967': 5, '1977': 6, '1993': 7, '1965': 8, ...},
'class': {'[PAD]': 0, 'Animation': 1, "Children's": 2, 'Comedy': 3, 'Action': 4, 'Adventure': 5, 'Thriller': 6, 'Drama': 7, 'Crime': 8, ...}}
映射后,样本信息的存放格式如下。
用户特征以
{'user_id': tensor([ 0, 1, ...942, 943]),
'age': tensor([ 0, 12, 8, ..., 14, 24]),
'gender': tensor([0, 1, 2, 1, ... 2, 2, 1]),
'occupation': tensor([ 0, 13, 3, ..., 14, 5]),
'zip_code': tensor([ 0, 35, 17...777, 792])}
物品特征以
{'item_id': tensor([ 0, 1, ...81, 1682]),
'movie_title': tensor([[ 0, 0,...0, 0]]),
'release_year': tensor([ 0, 3, 39, ..., 2, 1]),
'class': tensor([[ 0, 0, 0,... 0, 0]])}
交互特征以
{'user_id': tensor([ 1, 1, ...943, 943]),
'item_id': tensor([551, 650, 52... 13, 181]),
'rating': tensor([5., 4., 4., ..., 2., 5.]),
'timestamp': tensor([8.8125e+08, ...7505e+08])}
3. 数据集预处理流程
ALSDataset类并不是简单地加载并复制原始数据集文件。在组织好所有数据并交给模型之前,ALSDataset类对读入的原始数据进行了一系列的预处理。 预处理的具体过程不是固定的,具体的处理策略要根据config的设置来执行(修改config的方式视ALSDataset类的使用形式而定,在“快速开始”、“模块化模型设计”和“高级使用”中都有相应的介绍,这里不展开叙述)。本例使用的数据集同上,ALSDataset类的config为:
{'url': 'recstudio:dataset_demo/ml-100k',
'user_id_field': 'user_id:token',
'item_id_field': 'item_id:token',
'rating_field': 'rating:float',
'time_field': 'timestamp:float',
'time_format': None,
'inter_feat_name': 'ml-100k.inter',
'inter_feat_field': ['user_id:token', 'item_id:token', 'rating:float', 'timestamp:float'],
'inter_feat_header': 0,
'user_feat_name': ['ml-100k.user'],
'user_feat_field': [['user_id:token', 'age:token', 'gender:token', 'occupation:token', 'zip_code:token']],
'user_feat_header': 0,
'item_feat_name': ['ml-100k.item'],
'item_feat_field': [['item_id:token', 'movie_title:token_seq:" "', 'release_year:token', 'class:token_seq:" "']],
'item_feat_header': 0,
'field_separator': '\t',
'min_user_inter': 0,
'min_item_inter': 0,
'field_max_len': None,
'rating_threshold': None,
'ranker_rating_threshold': 3,
'drop_low_rating': None,
'max_seq_len': 20,
'save_cache': False}
下面介绍预处理的流程。
- 读入样本信息:
- ALSDataset读入交互数据,并剔除特征不完整的样本,保存在self.inter_feat中。 如果原数据集中没有rating_field(评分特征),那么添加一列rating,所有值为1(隐式反馈)。
- ALSDataset分别读入用户、物品特征,然后填充空值,分别保存在self.user_feat和self.item_feat中。 float型特征会用该特征均值填充,token型特征会用'[PAD]'(如果该特征已经映射过则使用0)填充, float_seq和token_seq分别用对应数据类型的空numpy array来填充。
- 过滤样本信息:
- 根据self.config['rating_threshold'],对评分进行处理。
- 对于self.inter_feat(交互数据)中[self.fuid, self.fiid]重复的项(即同一用户对同一商品的交互),只保留第一项,去掉其余项。
- 剔除交互很少的用户和物品。
- 从样本中剔除交互物品数少于self.config['min_user_inter']的用户
- 再剔除交互用户数少于self.config['min_item_inter']的物品。
- 由于第 ii 步可能减少一些用户的交互数,导致又出现一些新的用户,其交互物品数少于阈值,所以循环执行前两步直到所有用户和物品的交互数都不低于阈值。
- 把tokens映射到从0开始的index上(实际上是从1开始,因为'[PAD]'会被映射到0)。映射字典会保存在self.field2token2idx中,该字典的形式上文已展示过。
- 一些具体特征的处理:
- 对self.inter_feat的时间戳(如果有)的数据类型检验。特别地,如果为str型,检验其是否与self.config['time_format']匹配,并进行相应的解析;如果不匹配则报错。
- 对self.user_feat和self.item_feat,分别根据user_id和item_id从小到大的顺序进行重排。
- 根据模型的split_mode、ratio_or_num和shuffle参数进一步处理self.inter_feat(交互数据),并进行训练集/验证集/测试集划分:
- ratio_or_num用来划分训练集/验证集/测试集,为list型(其中的三个元素均为float型,分别代表训练集、验证集、测试集的比例)。
- 如果split_mode为'user_entry',那么先按用户分组,然后每个用户的交互信息(self.inter_feat)按ratio_or_num进行划分。如果shuffle为True,那么每个用户的交互信息在划分前会先打乱顺序。
- 如果split_mode为'user',那么按用户划分,即所有用户按ratio_or_num划分成三组,作为训练集/验证集/测试集。
- 如果split_mode为'entry',那么所有用户的交互信息(self.inter_feat)混在一起,按ratio_or_num进行划分。如果shuffle为True,那么所有的的交互信息在划分前会先打乱顺序。
4. 样本包含的特征
- 训练阶段的每个数据样本对应一个用户、所有历史交互物品及其评分,而验证/测试阶段的每个数据样本对应一个用户、正负例物品各一个及其对应评分、该用户的所有历史交互物品。
适用场景
数据集类型 | 适用任务 | 数据形式 | 适用模型 |
---|---|---|---|
TripletDataset | General Recommendation 和 CTR 任务 | (用户,物品,交互)三元组 | MF类模型(例如BPRMF)和FM类模型(例如 DCN) |
UserDataset | AutoEncoder 相关任务 | (用户,物品,交互,历史) | AE类模型(例如MultiVAE) |
SeqDataset | Sequential Recommendation 任务 | (用户,物品,交互,序列) | Seq类模型(例如SASRec) |
ALSDataset | Alternating Least Squares 相关任务 | $(u,I_u)$和$(i,U_i)$交替提供 | MF类模型(例如CML) |