基于PaddleRec框架Mind推荐算法实现穿衣搭配推荐

1 项目简介

1.1 引言

2015年淘宝举行穿衣搭配比赛时,基于统计分析,数据挖掘的推荐系统已开始大规模落地应用。这几年,随着机器学习,尤其是深度学习的迅猛发展,出现了很多推荐算法。本项目对淘宝穿衣搭配数据进行了整理和分析,用MIND算法实现召回。

2 数据查看

2.1 数据集概览

淘宝的穿衣搭配数据由图片和txt文件两部分组成
* 49 8823张图片
* 5个 txt 文件

TXT文件名dim_fashion_matchsetsdim_itemsexample_resulttest_itemsuser_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_sizeepoch_numRecall@50NDCG@50HitRate@50Time of each epoch
mind(paddle实现)256100.00997%0.00725%0.02790%20.56s(CPU)

总结与展望

本项目使用PaddleRec框架Mind推荐算法,对淘宝穿衣搭配客户购买商品历史数据整理后用MIND算法实现对穿衣搭配召回,完成推荐。
在对淘宝穿衣搭配数据集整理,分析之后,可以借鉴计算机视觉的前沿算法模型进行优化。

本项目在选择算法框架,改进激活函数等所有实现推荐系统的关键过程均由张宏理导师指导。

这次特训营有幸能得到项目导师 张宏理 的指导,随时解答,指导详细,学到很多,在此

张宏理 老师表示由衷的感谢!

感谢aistudio平台、特训营提供的学习机会

此文章为搬运
原项目链接

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐