基于PaddleRec框架Mind推荐算法实现穿衣搭配推荐
1 项目简介
1.1 引言
2015年淘宝举行穿衣搭配比赛时,基于统计分析,数据挖掘的推荐系统已开始大规模落地应用。这几年,随着机器学习,尤其是深度学习的迅猛发展,出现了很多推荐算法。本项目对淘宝穿衣搭配数据进行了整理和分析,用MIND算法实现召回。
2 数据查看
2.1 数据集概览
淘宝的穿衣搭配数据由图片和txt文件两部分组成
* 49 8823张图片
* 5个 txt 文件
TXT文件名 | dim_fashion_matchsets | dim_items | example_result | test_items | user_bought_history |
内容 | 23105种商品套餐 | 499983个商品,所属类别,关键词 | 比赛样本提交示例 | 5462个测试样本 | 13611038条用户,购买商品,日期数据 |
2.2 检测数据样本和图片
考虑到全部图片数据集解压时间长,影响项目体验,本项目仅展示所需图片。
如需查看全部图片,请移步 淘宝2015年穿衣搭配比赛数据套餐图片展示
%cd /home/aistudio/work/
!unzip -qo /home/aistudio/data/data183338/PaddleRecMind.zip -d /home/aistudio/work/
/home/aistudio/work
查看套餐图片
import matplotlib.pyplot as plt
import random
import os
import time
clothes_dir = '/home/aistudio/work/ClothesMatching'
if not os.path.exists(clothes_dir):
os.mkdir(clothes_dir)
img_path = clothes_dir + '/images'
match_path = clothes_dir + '/dim_fashion_matchsets.txt'
item_path = clothes_dir + '/dim_items.txt'
class show_img():
def __init__(self):
super(show_img, self).__init__()
def get_fashion_match(self, coll_id):# coll_id 搭配套餐ID
with open(match_path, 'r') as f:
lines = f.readlines()
desired_line = lines[coll_id-1:coll_id]
fashion_match_line = desired_line[0].rstrip('\n').split(' ')
# print(type(fashion_match_line))
items_list = fashion_match_line[1].split(';')
print(items_list)
return items_list
# 展示套餐图片
def show_fashion_match_item(self, items_list):
list_len = len(items_list)
if list_len == 1:
self.draw_plt_imgs(items_list)
else:
for i in range(len(items_list)):
item_str_list = items_list[i].split(',')
self.draw_plt_imgs(item_str_list)
# 导出列表中图片
def draw_plt_imgs(self, items_list):
list_len = len(items_list)
for img_id in range(list_len):
img_index_str = items_list[img_id]
path = img_path + '/'+ img_index_str +'.jpg'
img = plt.imread(path)
# 行 列数 img下标从1开始
plt.subplot( 3, 4, img_id + 1)
plt.imshow(img)
plt.xticks([])# 消除每张图片自己的纵横坐标
plt.yticks([])
plt.show()
# coll_id = random.randint(a=0,b=23105) #dim_fashion_matchsets 共有23105套搭配
coll_id = 2733 #输入id查询套餐图片
showImg = show_img()
items_list = showImg.get_fashion_match(coll_id)
print('第一次运行有可能不显示图片,请稍等5-10秒,重新运行即可')
showImg.show_fashion_match_item(items_list)
通过对图片的查看可以把套餐大致归为几类
1 相似度搭配
第 1598 套餐
2 穿搭配套搭配
第 5602 套餐
第 19169 套餐
3 相似度搭配 + 穿搭配套 搭配
第 2733 套餐
第 5899 套餐
第 9340 套餐
第 4491 套餐
4 爆款或者季节性搭配,例如冬季保暖
第 21738 套餐
5 无法确定是否搭配
第 10914 套餐
6 相似度搭配 + 穿搭 类似的
第 8983 套餐
7 不搭配
第 22953 套餐
第 18515 套餐
第 5522 套餐
查看22953套餐可以看到是一个连衣裙和牛仔裤
可能不懂2015年的时尚。。。实在看不出这套餐里面的东西有什么关联。。。
coll_id = 22953 #输入id查询套餐图片
showImg = show_img()
items_list = showImg.get_fashion_match(coll_id)
showImg.show_fashion_match_item(items_list)
3 数据整理和分析
3.1 数据整理
3.1.1 商品类别整理 dim_items.txt
商品类别共计 281个 出现 499 983 次, 关键分词 共计 85 488出现7 043 222次
商品编号为 2613332 368 有类别无关键词 需要根据类关键词填补,或是丢弃。 因为只有一个空值,选择丢弃。
3.1.2 套餐数据整理
参考上面图片展示
3.1.3 用户购买历史整理
user_bought_history.txt 文件其实是两个文件的合并,不可照商品排序排序统计分析,需要处理
用户购买历史样本取值时间为 [2014年6月14 - 2015年6月15日] 总计367天
共计 1 103 702个用户 购买 13 611 038人次 462 008 种商品
商品总计 499 983种 有 499983 - 462008 种商品从未被购买 有商品类别和关键词,无被购买记录
3.1.4 竞赛测试数据整理
需要提交的 test item 总计 5462 其中[2448847, 2967407, 3051577] 没有对应的图片 是否需要GAN?
3.2 数据问题和分析
3.2.1 一年之内购买商品次数少于5次的,其购买商品极少内在关联
进行数据清理
3.2.2 商品相关性是否需要考虑购买时段,比如,一个很少购物的用户一周内如果有重复购买衣物,大多数是相似或者多兴趣集配套衣物。
统计全部时段计算量极大,暂时无法实现,可以根据购此买商品的其他用户进行推荐
3.2.3 如何处理购买商种类少,购买次数多的数据?
用户一年内只够买一种或几种商品,购买次数多达百次千次的,进行清理
3.2.4 很多重复购买的物品是替代家人朋友购买,推荐模型是否考虑用户是个体还是群体的差异?
对重复购买提高商品排序,但是不重复计入 详见代码 一些推荐算法数据集也有相关问题,
例如 Amazonbook点击数据最大的单用户点击量达到22000次以上。
3.2.5 是否需要根据类别,关键词分析出季节性,增加测试的准确性?
每一个商品和类别放到时间轴上进行时序分析计算量太大,但是可在推荐中加入当季或者月内爆款商品
3.2.6 是否需要考虑套餐的交叉集合商品,增加测试结果的准确性?
比如 1051558 1621029
4 算法选择和模型搭建
4.1 通过对数据分析,和测试数据的要求,可以确定竞赛模型任务: 为相关类别,有关键词属性的,被不同用户购买的商品 推荐 200个商品
4.2 本项目指导老师 张宏理 推荐参考Paddle框架PaddleRec推荐系统的MIND算法,对激活函数和参数进行优化和调节,如果时间允许,融合根据数据分析的自建算法,在有可能的情况下考虑GNN。
推荐的流程主要分为召回阶段和排序阶段。召回阶段负责提供用户兴趣相关的物品,之后,排序阶段预测用户与这些候选物品交互的精确概率。MIND算法做的是召回阶段的工作。
MIND(Multi-Interest Network with Dynamic Routing)用胶囊网络与动态路由机制在召回阶段建立用户多兴趣模型,
具体实现是用网络建立多兴趣提取层,使用动态路由机制将用户的点击或者购买历史聚类,然后每个类簇中产生一个表示向量,多个类簇的多个向量合起来,表示用户的兴趣集。4.3 MIND 中的多兴趣 胶囊算法 动态路由可谓推荐算法中特别有参考价值的算法,所以本项目搭建的模型以其作为开始,实现推荐后再进行优化,增加和融合提高竞赛测试准确率的算法。
5 训练, 验证, 测试数据集制作
import os
num_bought = 2 # 购买商品数量 index [0, 5)
min_num_item = 2 # 设定数据购买次数
max_num_item = 50 # min_min = 0 # max_max = 60|
in_txt_path = '/home/aistudio/work/ClothesMatching/user_bought_history.txt'
def write_graph_txt(file_path, graph, start_index):
i = start_index
_file = open(file_path, 'a')
for uid, item_list in graph.items():
for d_i in range(len(item_list)):
_file.write( str(i) + ',' + str(item_list[d_i]) + ',' + str(d_i) + '\n')
i += 1
_file.close()
def load_graph(source):# 返回用户 商品id 日期 # low 4620210 10509214 1 20140619
graph = {}
with open(source) as fr:
for line in fr:
conts = line.strip().split(' ')
user_id = int(conts[0])
item_id = int(conts[1])
time_stamp = int(conts[2])
if user_id not in graph:
graph[user_id] = []
graph[user_id].append([item_id, time_stamp])
print(len(graph))#1103702
return graph
# itme商品去重 日期时间戳排序
def remove_duplicate(graph):
out_graph = {}
for u_id, value in graph.items():
if len(value) < num_bought:# 过滤购买次数少于num_bought=4的用户
continue
value.sort(key=lambda x: x[0])# 按商品排序
value.append((0,0)) # 在尾部追加一组 以便排序后遍历时 (0 - len-1)比较是否重复
out_graph[u_id] = []
re_sum = 0
for i in range(len(value)-1):
if value[i][0] != value[i+1][0]:
out_graph[u_id].append([ value[i][0], value[i][1] ])
re_sum = 0
else:
value[i+1][1] = value[i][1] - (367 * re_sum)
re_sum += 1 #累加遇重减去日期天数 item时间戳靠前
print(len(graph))#1103702
return out_graph
def sort_date(graph):#按日期排序
out_graph = {}
# i = 0
for uid, value in graph.items():
# i += 1
# if i%2:#减少一半
# continue
# 过滤购买商品种类少于 min_num_item = 4 大于 20的非随机用户
# if len(value) > max_num_item:
# continue
# if len(value) < min_num_item:
# continue
out_graph[uid] = []
value.sort(key=lambda x: x[1]) #按日期排序
out_graph[uid] = [x[0] for x in value]
print(len(out_graph))
return out_graph
def sort_item(graph):#商品id排序
out_graph = {}
item_dict = {}
item_index = 0
for uid, value in graph.items():
out_graph[uid] = []
for item in value:
if item not in item_dict:
# print(item)
item_dict[item] = item_index
out_graph[uid].append(( item_index ))
item_index += 1
else:
item_id = item_dict.get(item)
out_graph[uid].append(( item_id ))
print(len(item_dict))
print(len(out_graph))
return out_graph, item_dict
def generate_train_valid_test(graph):
item_set = {0}
train_graph = {}
valid_graph = {}
test_graph = {}
i = 0
train_len, valid_len, test_len = 0, 0, 0
for uid, items in graph.items():
i += 1
if i == 7:
valid_len += len(items)
if (valid_len < 477748):
valid_graph[uid] = [x for x in items ]
elif i > 8:
i = 0
test_len += len(items)
if (test_len < 474814):
test_graph[uid] = [x for x in items ]
else:
train_len += len(items)
if (train_len < 7005582):
train_graph[uid] = [x for x in items ]
return train_graph, valid_graph, test_graph
def show_graph_data(graph):
count = 0
for uid, item_list in graph.items():
print(uid, item_list)
count += 1
if count == 3: break
ubd_graph = load_graph(in_txt_path)
print('load graph...')
res_graph = remove_duplicate(ubd_graph)
del ubd_graph
print('remove_duplicate....')
sort_date_graph = sort_date(res_graph)
del res_graph
print('sort_date.....')
sort_graph, item_dict = sort_item(sort_date_graph)
del sort_date_graph
print('sort_item......')
train_graph, valid_graph, test_graph = generate_train_valid_test(sort_graph)
del item_dict
del sort_graph
%cd /home/aistudio/work/ClothesMatching
!rm -rf train
!mkdir train
start_index = 0
write_graph_txt('train/train.txt', train_graph, start_index)
start_index = start_index + len(train_graph)
write_graph_txt('valid.txt', valid_graph, start_index)
start_index = start_index + len(valid_graph)
write_graph_txt('test.txt', test_graph, start_index)
del start_index
del train_graph
del valid_graph
del test_graph
/home/aistudio/work/ClothesMatching
# %cd /home/aistudio/work/ClothesMatching
# !cp train/train.txt ./
# !rm -rf 'train_item_num.txt'
# !python merge_count_item.py
# %cd /home/aistudio/work
# !cat config_bigdata.yaml # 每次生成据训练和验证数据 需train_item_num 修改config_bigdata参数
# %cd /home/aistudio/work/ClothesMatching
# !rm valid
# !mkdir valid
# !python preprocess.py
6 模型训练
%cd /home/aistudio/work/ClothesMatching
!pip install --upgrade pip
!python -m pip install --no-index --find-links=/home/aistudio/work/pip_down/ -r requirements.txt
%env KMP_DUPLICATE_LIB_OK=TRUE
%cd /home/aistudio/work
!python -u tools/trainer.py -m config_bigdata.yaml # 动态图训练
%cd /home/aistudio/work
!python -u infer.py -m config_bigdata.yaml -top_n 200 #全量数据运行config_bigdata
%cd /home/aistudio/work
!python -u infer.py -m config_bigdata.yaml -top_n 50
在全量数据下模型的top 200指标如下:
epoch: 9 done, recall@200: 0.00997, ndcg@200: 0.00725, hitrate@200: 0.02790, epoch time: 20.56 s
模型 | batch_size | epoch_num | Recall@50 | NDCG@50 | HitRate@50 | Time of each epoch |
mind(paddle实现) | 256 | 10 | 0.00997% | 0.00725% | 0.02790% | 20.56s(CPU) |
总结与展望
本项目使用PaddleRec框架Mind推荐算法,对淘宝穿衣搭配客户购买商品历史数据整理后用MIND算法实现对穿衣搭配召回,完成推荐。
在对淘宝穿衣搭配数据集整理,分析之后,可以借鉴计算机视觉的前沿算法模型进行优化。本项目在选择算法框架,改进激活函数等所有实现推荐系统的关键过程均由张宏理导师指导。
这次特训营有幸能得到项目导师 张宏理 的指导,随时解答,指导详细,学到很多,在此
对 张宏理 老师表示由衷的感谢!
感谢aistudio平台、特训营提供的学习机会