目錄
一.引言
二.訂單數(shù)據(jù)預(yù)處理
1.數(shù)據(jù)樣例
2.訂單數(shù)據(jù)處理
3.用戶 Session 構(gòu)建
三.構(gòu)造 sku_id 游走序列
1.獲取完整 Session List
2.統(tǒng)計 sku_id 轉(zhuǎn)移詞頻
3.構(gòu)建 sku_id 圖
4.游走構(gòu)造 sku 序列
四.商品側(cè)信息預(yù)處理
1.讀取商品信息
2.Left Join 匹配側(cè)信息
3.Id2Index 構(gòu)建
五.基于 Ngram 與 Negative Sample 的樣本生成
1.自定義 Ngram 樣本生成
2.keras.preprocessing.sequence.skipgrams 樣本生成
3.tf.random.log_uniform_candidate_sampler 采樣負樣本
六.總結(jié)
一.引言
上一篇文章?EGES 與推薦系統(tǒng)用戶冷啟動?一文中我們針對 EGES 論文中提到的一些關(guān)鍵要素進行了分析,在 Word2vec 的基礎(chǔ)上,其創(chuàng)新點主要分為兩個部分:
? 通過 Session 構(gòu)建用戶序列代替
? 在原有數(shù)據(jù)中引入 Side Info 側(cè)信息提高 Emb?表達能力
本文將主要基于訂單數(shù)據(jù)介紹 EGES 模型的數(shù)據(jù)準備工作,后續(xù)會通過 Keras 基于該數(shù)據(jù)進行后續(xù)的 EGES 模型構(gòu)建。
二.訂單數(shù)據(jù)預(yù)處理
1.數(shù)據(jù)樣例
數(shù)據(jù)處理前,首先看下數(shù)據(jù)的原始樣式:
- 訂單數(shù)據(jù)
user_id,sku_id,action_time,module_id,type
937922,357022,2018-02-04 08:28:15,8107857,1
937922,73,2018-02-04 08:27:07,8107857,1
937922,29583,2018-02-04 08:26:31,8107857,1
937922,108763,2018-02-04 08:26:10,8107857,1
1369473,331139,2018-02-03 21:55:49,3712240,1
1330642,69016,2018-02-01 12:47:23,1844129,1
1330642,211690,2018-02-01 12:48:50,1844129,1
1330642,322692,2018-02-01 12:48:15,1844129,1
1330642,19643,2018-02-01 12:47:55,1844129,1
這里原始數(shù)據(jù)包含 user_id、sku_id、action_time、module_id 以及 type,后續(xù)我們主要關(guān)注
??sku_id
商品id,可以理解為后續(xù) word2vec 里的 word,因為我們要根據(jù) sku_id 構(gòu)建用戶行為序列
??action_time
訂單時間,主要用于判定不同 Session 之間的間隔,從而劃分不同的 Session 與 Seq
??type
行為類型,例如點擊、購買等等,該值后續(xù)應(yīng)用于 Session 的判斷,當 type 表示付款下單時,我們可以認為是一次購買行為的結(jié)束,即 Session 的分割點
- 商品信息
sku_id,brand,shop_id,cate,market_time
226519,6302,2399,79,2015-07-02 11:19:04.0
63114,9167,4216,79,2016-07-08 14:29:12.0
372345,2748,7125,79,2016-04-07 16:21:40.0
366931,2698,10252,79,2016-09-11 15:00:22.0
174979,8368,871,79,2017-12-06 17:56:17.0
295436,6302,2399,79,2015-07-02 11:19:04.0
282251,6302,2399,79,2015-07-02 11:19:04.0
146764,6302,2399,79,2015-07-02 11:19:04.0
130851,6302,2399,79,2015-07-02 11:19:04.0
這里包含 sku_id、brand、shop_id、cate 與 market_time,除去 market_time,brand-品牌、shop_id-店鋪以及 cate-商品標簽 都可以視作是商品的 SideInfo 即 EGES 要引入的商品側(cè)信息,所以后續(xù)除了通過訂單數(shù)據(jù)獲取用戶的游走序列外,我們還需要為目標詞匹配其 Side Info。
2.訂單數(shù)據(jù)處理
# dropna() 函數(shù)的作用是去除讀入的數(shù)據(jù)中(DataFrame)含有NaN的行,這里 axis 默認為0即刪除含 Nan 的行
# parse_dates 解析時間列 處理后包含 user_id sku_id action_time type 信息
action_data = pd.read_csv(args.data_path + 'action_head.csv', parse_dates=['action_time']) \
.drop('module_id', axis=1).dropna()
# 獲取全部 sku_id 銷售品編碼,此處共 34048 個 Sku
all_skus = action_data['sku_id'].unique()
sku_num = len(list(all_skus))
all_skus = pd.DataFrame({'sku_id': list(all_skus)})
# 將 sku 編碼到 0-34047 的 id
sku_lbe = LabelEncoder()
all_skus['sku_id'] = sku_lbe.fit_transform(all_skus['sku_id'])
action_data['sku_id'] = sku_lbe.transform(action_data['sku_id'])
print('make session list\n')
訂單數(shù)據(jù)邏輯比較清晰:
A.讀取 csv 文件,解析訂單 action_time,去除無關(guān)列 module_id 并過濾掉包含 Nan 的異常數(shù)據(jù)
B.對 sku_id 去重獲取當前訂單中所有 sku_id 的去重集合
C.通過 LabelEncoder 對 sku_id 編碼并更新到對應(yīng)的 DataFrame 列中
經(jīng)過這一步數(shù)據(jù)處理,原始訂單數(shù)據(jù)存儲到 Python 的 DataFrame 中,其樣式為:
Tips:
論文中還給出更為細節(jié)的異常數(shù)據(jù)過濾方法:
??去除非置信正樣本
點擊后的停留時間小于1秒,則該點擊可能是無意的,需要刪除,這里其實針對的是用戶誤點擊或者誤觸造成的不置信的正樣本。
??過度活躍樣本
淘寶有一些 "過度活躍" 的用戶,他們實際上是垃圾用戶。根據(jù)我們在淘寶的長期觀察,如果一個用戶在不到 3 個月的時間里購買了1000 件商品或者總點擊量超過 3500 次,那么這個用戶很有可能是一個垃圾用戶。
??高頻更新樣本
淘寶上的零售商不斷更新商品的詳細信息。在極端的情況下,一件商品在經(jīng)過長時間的更新后,在淘寶上的同一個標識符可能會變成完全不同的商品。因此,我們刪除了同一個標識下不斷更新的 Item,以免其語義變化對模型推理引入噪聲。
上述三個條件需要端上記錄更多點擊時長或者商品的更新歷史等,大家在實際應(yīng)用場景中也可以參考并加入數(shù)據(jù)清洗的流程中。
3.用戶 Session 構(gòu)建
基于上面生成的訂單信息 DF 我們可以結(jié)合 type 和 action_time 截取用戶行為 Session,實戰(zhàn)中 Alibaba 的 Session Gap 推薦為 1h,除此之外還需要結(jié)合 use_type 即可用訂單類型:
session_list = get_session(action_data, use_type=[1, 2, 3, 5])
下面詳細看下 get_session 方法的實現(xiàn):
def get_session(action_data, use_type=None):
if use_type is None:
use_type = [1, 2, 3, 5]
# 選擇指定 use_type
action_data = action_data[action_data['type'].isin(use_type)]
# 由低到高升序排列,按 user 用戶、action_time 行為時間排列
action_data = action_data.sort_values(by=['user_id', 'action_time'], ascending=True)
# 按 user_id 聚合為 list
group_action_data = action_data.groupby('user_id').agg(list)
# 根據(jù) cut_session 函數(shù)按行處理
session_list = group_action_data.apply(cnt_session, axis=1)
return session_list.to_numpy()
這里邏輯也很清晰:
A.首先過濾原始數(shù)據(jù)中不符合的類型的 type 對應(yīng)的訂單
B.根據(jù) user_id 和 action_time 為用戶構(gòu)建時序訂單
C.將 user_id 的行為聚合為 list 供后續(xù)處理
?D.通過 cut_session 方法切分生成 Session
def cnt_session(data, time_cut=30, cut_type=2):
# 商品、時間、類型列表
sku_list = data['sku_id']
time_list = data['action_time']
type_list = data['type']
# 構(gòu)建 Session
session = []
tmp_session = []
# 遍歷每一個 Item 即 sku_id
for i, item in enumerate(sku_list):
# A.cut_type=2 即下單 B.Session Gap > 30min C.最后一個行為
if type_list[i] == cut_type \
or (i < len(sku_list) - 1 and (time_list[i + 1] - time_list[i]).seconds / 60 > time_cut) \
or i == len(sku_list) - 1:
tmp_session.append(item)
session.append(tmp_session)
tmp_session = []
else:
tmp_session.append(item)
return session
這里對每個用戶的 sku_id list 進行處理,其中設(shè)置了 3 個截斷條件用于結(jié)束 session 并添加到 session 候選集:
? cut_type=2?當前訂單行為為下單
? time_cut > 30 前后訂單超過預(yù)定閾值
? last_order 用戶 list 最后一個訂單
Tips:
通過上面的聚合與處理我們將用戶原始訂單信息生成為每一個用戶多個 Session 的情況,論文中使用 Session 也基于如下考慮:
??計算用戶完整歷史行為的計算與空間成本太高
??用戶的興趣會隨著時間的推移而變化
? 不同的 Session 內(nèi)用戶的興趣可能更加相近,從而強化 item 之間的相似性
三.構(gòu)造 sku_id 游走序列
通過前面劃分好的 Session,后續(xù)我們就可以基于 Session 內(nèi)的 item 商品序列構(gòu)建 Graph 實現(xiàn) GraphEmbedding 前置的游走工作,下面提供 RandomWalk 隨機游走和 Node2vec 兩種游走方式的實現(xiàn)。
1.獲取完整 Session List
session_list_all = []
for item_list in session_list:
for session in item_list:
if len(session) > 1:
session_list_all.append(session)
遍歷全部 Session,保留 Session 內(nèi) Item 數(shù)量大于 1 的會話。
2.統(tǒng)計 sku_id 轉(zhuǎn)移詞頻
node_pair = dict()
for session in session_list_all:
# 針對 (t,v) 統(tǒng)計轉(zhuǎn)移頻率
for i in range(1, len(session)):
if (session[i - 1], session[i]) not in node_pair.keys():
node_pair[(session[i - 1], session[i])] = 1
else:
node_pair[(session[i - 1], session[i])] += 1
這里對 Session 內(nèi)每個 (Node, Node) 進行詞頻統(tǒng)計。
# 出節(jié)點、入節(jié)點 (t, v): weight
in_node_list = list(map(lambda x: x[0], list(node_pair.keys())))
out_node_list = list(map(lambda x: x[1], list(node_pair.keys())))
weight_list = list(node_pair.values())
graph_df = pd.DataFrame({'in_node': in_node_list, 'out_node': out_node_list, 'weight': weight_list})
graph_df.to_csv('./data_cache/graph.csv', sep=' ', index=False, header=False)
基于上面的詞頻生成節(jié)點轉(zhuǎn)移表格,相當于生成節(jié)點轉(zhuǎn)移的帶權(quán)圖的原始數(shù)據(jù),weight 權(quán)重以節(jié)點出現(xiàn)的頻次為準。
為了方便后續(xù)快速使用,這里將原始的帶權(quán)圖信息存儲至 csv,后續(xù)可以直接加載避免多次數(shù)據(jù)預(yù)處理。
3.構(gòu)建 sku_id 圖
# networkx 讀取生成帶權(quán)圖
G = nx.read_edgelist('./data_cache/graph.csv', create_using=nx.DiGraph(), nodetype=None, data=[('weight', int)])
walker = RandomWalker(G, p=args.p, q=args.q)
# 生成 Alias 轉(zhuǎn)移概率表
print("Preprocess transition probs...")
walker.preprocess_transition_probs()
這里使用 networkx 讀取上面的數(shù)據(jù)生成帶權(quán)圖,再定義?RandomWalker 類,其實現(xiàn)如下:
class RandomWalker:
def __init__(self, G, p=1, q=1):
"""
:param G:
:param p: Return parameter,controls the likelihood of immediately revisiting a node in the walk.
:param q: In-out parameter,allows the search to differentiate between “inward” and “outward” nodes
"""
self.G = G
self.p = p
self.q = q
def deepwalk_walk(self, walk_length, start_node):
walk = [start_node]
while len(walk) < walk_length:
cur = walk[-1]
cur_nbrs = list(self.G.neighbors(cur))
if len(cur_nbrs) > 0:
walk.append(random.choice(cur_nbrs))
else:
break
return walk
def node2vec_walk(self, walk_length, start_node):
G = self.G
alias_nodes = self.alias_nodes
alias_edges = self.alias_edges
walk = [start_node]
while len(walk) < walk_length:
cur = walk[-1]
cur_nbrs = list(G.neighbors(cur))
if len(cur_nbrs) > 0:
if len(walk) == 1:
walk.append(
cur_nbrs[alias_sample(alias_nodes[cur][0], alias_nodes[cur][1])])
else:
prev = walk[-2]
edge = (prev, cur)
next_node = cur_nbrs[alias_sample(alias_edges[edge][0],
alias_edges[edge][1])]
walk.append(next_node)
else:
break
return walk
def simulate_walks(self, num_walks, walk_length, workers=1, verbose=0):
G = self.G
nodes = list(G.nodes())
results = Parallel(n_jobs=workers, verbose=verbose, )(
delayed(self._simulate_walks)(nodes, num, walk_length) for num in
partition_num(num_walks, workers))
walks = list(itertools.chain(*results))
return walks
def _simulate_walks(self, nodes, num_walks, walk_length, ):
walks = []
for _ in range(num_walks):
random.shuffle(nodes)
for v in nodes:
if self.p == 1 and self.q == 1:
walks.append(self.deepwalk_walk(
walk_length=walk_length, start_node=v))
else:
walks.append(self.node2vec_walk(
walk_length=walk_length, start_node=v))
return walks
def get_alias_edge(self, t, v):
"""
compute unnormalized transition probability between nodes v and its neighbors give the previous visited node t.
:param t:
:param v:
:return:
"""
G = self.G
p = self.p
q = self.q
unnormalized_probs = []
for x in G.neighbors(v):
weight = G[v][x].get('weight', 1.0) # w_vx
if x == t: # d_tx == 0
unnormalized_probs.append(weight / p)
elif G.has_edge(x, t): # d_tx == 1
unnormalized_probs.append(weight)
else: # d_tx > 1
unnormalized_probs.append(weight / q)
norm_const = sum(unnormalized_probs)
normalized_probs = [
float(u_prob) / norm_const for u_prob in unnormalized_probs]
return create_alias_table(normalized_probs)
def preprocess_transition_probs(self):
"""
Preprocessing of transition probabilities for guiding the random walks.
"""
G = self.G
alias_nodes = {}
for node in G.nodes():
unnormalized_probs = [G[node][nbr].get('weight', 1.0) # 保存start的鄰居節(jié)點的權(quán)重
for nbr in G.neighbors(node)]
norm_const = sum(unnormalized_probs)
normalized_probs = [
float(u_prob) / norm_const for u_prob in unnormalized_probs] # 計算從node到鄰居的轉(zhuǎn)移矩陣
alias_nodes[node] = create_alias_table(normalized_probs)
alias_edges = {}
for edge in G.edges():
alias_edges[edge] = self.get_alias_edge(edge[0], edge[1])
self.alias_nodes = alias_nodes
self.alias_edges = alias_edges
return
通過調(diào)用 RandomWalker 的 preprocess_transition_probs 方法可以生成對應(yīng) Graph 的 Node Alias 概率轉(zhuǎn)移表與 Edge Alias 轉(zhuǎn)移表,基于轉(zhuǎn)移表與 Alias 采樣就可以進行后續(xù)的游走序列生成了。這里 RandomWalker 包含了兩個參數(shù) p、q 其實是 Node2vec 中定義游走偏向 BFS 還是 DFS 的參數(shù),如果 p=q=1 則游走退化為簡單的隨機游走。
4.游走構(gòu)造 sku 序列
# Parallel 多線程實現(xiàn) Session 內(nèi)的 Item 序列游走, P=Q=1 時為 DeepWalk,反之為 Node2vec
session_reproduce = walker.simulate_walks(num_walks=args.num_walks, walk_length=args.walk_length, workers=4,
verbose=1)
# 保留長度大于2的游走序列
session_reproduce = list(filter(lambda x: len(x) > 2, session_reproduce))
這里使用了 Python 的多線程方式加速序列生成的速度并保留長度大于2的游走序列。
? simulate_walks
def simulate_walks(self, num_walks, walk_length, workers=1, verbose=0):
G = self.G
nodes = list(G.nodes())
results = Parallel(n_jobs=workers, verbose=verbose, )(
delayed(self._simulate_walks)(nodes, num, walk_length) for num in
partition_num(num_walks, workers))
walks = list(itertools.chain(*results))
return walks
該方法通過 Graph 獲取全部 Nodes,隨后使用 Parallel 多線程執(zhí)行序列游走生成方法,真實調(diào)用的是 _simulate_walks,兩個方法相差一個 "_" 。
? _simulate_walks
def _simulate_walks(self, nodes, num_walks, walk_length, ):
walks = []
for _ in range(num_walks):
random.shuffle(nodes)
for v in nodes:
if self.p == 1 and self.q == 1:
walks.append(self.deepwalk_walk(
walk_length=walk_length, start_node=v))
else:
walks.append(self.node2vec_walk(
walk_length=walk_length, start_node=v))
return walks
如前面所說,當 p=q=1 時,使用 deepwalk 即常規(guī)的隨機游走生成序列,否則采用 node2vec 生成游走序列,最后返回全部游走結(jié)果列表 walks,下圖為部分游走序列,其中 id 代表 sku_id。
四.商品側(cè)信息預(yù)處理
1.讀取商品信息
# 添加 SideInfo 側(cè)信息
# 主要包含每個 sku_id 的 brand,shop_id,cate 品牌、店鋪、標簽
product_data = pd.read_csv(args.data_path + 'jdata_product.csv').drop('market_time', axis=1).dropna()
讀取商品側(cè)信息文件,去除無關(guān)的 'market_time' 列并刪除 Nan 的列,最后將 sku_id 根據(jù)前面得到的 LabelEncoder 進行編碼轉(zhuǎn)化,這里共有?34048 個 sku 商品。
Tips:
這里的 sku_id 還未編碼,所以并不能與前面 LabelEncoder 編碼過的 sku_id 一一對應(yīng)。
2.Left Join 匹配側(cè)信息
# 采用 LeftJoin 拼接 Product 信息,其中默認值填充 0
all_skus['sku_id'] = sku_lbe.inverse_transform(all_skus['sku_id'])
print(str(all_skus.count()))
sku_side_info = pd.merge(all_skus, product_data, on='sku_id', how='left').fillna(0)
為了 Left Join 為剛才的商品匹配 Side info 側(cè)信息,這里需要首先將 all_skus 的 sku_id 通過 inverse_transform 反編碼為原始狀態(tài),隨后與 product_data 側(cè)信息進行 Left Join 匹配,默認填充值為 0。
匹配后我們就可以得到樣本中出現(xiàn)過的 sku item 的全部側(cè)信息了。
3.Id2Index 構(gòu)建
與 sku_id 同理,brand、shop_id 和 cate 也無法保證連續(xù)性,因此也需要通過 LabelEncoder 進行編碼從而方便后續(xù)的模型訓練。
# featNum
feat_num_list = []
# id2index
for feat in sku_side_info.columns:
if feat != 'sku_id':
lbe = LabelEncoder()
sku_side_info[feat] = lbe.fit_transform(sku_side_info[feat])
feat_num_list.append([feat, len(list(lbe.classes_))])
else:
sku_side_info[feat] = sku_lbe.transform(sku_side_info[feat])
feat_num_list.append([feat, len(list(sku_lbe.classes_))])
sku_side_info = sku_side_info.sort_values(by=['sku_id'], ascending=True)
sku_side_info.to_csv('./data_cache/sku_side_info.csv', index=False, header=False, sep='\t')
最后緩存至 csv 文件中以備后續(xù)使用,可以看到編碼后各個類型數(shù)據(jù)的數(shù)量:
[['sku_id', 34048], ['brand', 3663], ['shop_id', 4786], ['cate', 80]]
五.基于 Ngram 與 Negative Sample 的樣本生成
1.自定義 Ngram 樣本生成
all_pairs = get_graph_context_all_pairs(session_reproduce, args.window_size)
np.savetxt('./data_cache/all_pairs', X=all_pairs, fmt="%d", delimiter=" ")
自定義方法也很好理解,遍歷每一個 Seq 的每一個 Sku,基于中心詞 target sku 構(gòu)建 window_size 的窗口,隨后將 target sku 與 window 范圍內(nèi)的 context sku 構(gòu)成多組 (target, context) 樣本保存下來。
def get_graph_context_all_pairs(walks, window_size):
all_pairs = []
# 每一個序列
for k in range(len(walks)):
# 每一個 word
for i in range(len(walks[k])):
# 添加 Item -> Target 邏輯
for j in range(i - window_size, i + window_size + 1):
if i == j or j < 0 or j >= len(walks[k]):
continue
else:
all_pairs.append([walks[k][i], walks[k][j]])
return np.array(all_pairs, dtype=np.int32)
但是常規(guī)方法只生成了正樣本,還需要我們基于 sku 數(shù)量進行全局的負采樣,不過這里 kreas 已經(jīng)提供了現(xiàn)成的 Ngram 帶負采樣的方法,下面我們嘗試一下。
2.keras.preprocessing.sequence.skipgrams 樣本生成
all_pairs = []
labels = []
num_ns = 5
SEED = 99
for seq in session_reproduce[0:1000]:
skip_grams, ys = tf.keras.preprocessing.sequence.skipgrams(
seq,
vocabulary_size=sku_num,
window_size=args.window_size,
negative_samples=5)
all_pairs.extend(skip_grams)
labels.extend(ys)
all_pairs 存儲所有 (target, context) 對,num_ns 代表負采樣的數(shù)量,一般為 5-20,seq 為 待采樣序列,window_size 為窗口大小,其關(guān)于正樣本的采樣思路與上面的自定義方法一致,不同的是內(nèi)置了負采樣機制,可以直接得到正負樣本。下圖為 skipgrams 采樣得到的 10 個樣本與其對應(yīng) label:
3.tf.random.log_uniform_candidate_sampler 采樣負樣本
細心的小伙伴會發(fā)現(xiàn)上述方法中,如果參數(shù) negative_sampels = 0 時,得到的就全是正樣本對,結(jié)合?tf.random.log_uniform_candidate_sampler 對每一個正樣本進行一次負采樣也未嘗不可。
- 獲取全部正樣本
all_pairs = []
targets, contexts, labels = [], [], []
positive_skip_grams, ys = tf.keras.preprocessing.sequence.skipgrams(
seq,
vocabulary_size=sku_num,
window_size=args.window_size,
negative_samples=0)
與上面一致,區(qū)別是 negative_samples 參數(shù)設(shè)置為 0 即不采樣負樣本。
- 根據(jù)正樣本獲取負樣本
for target_word, context_word in positive_skip_grams:
context_class = tf.reshape(tf.constant(int(context_word), dtype='int64'), (1, 1))
negative_sampling_candidates, _, _ = tf.random.log_uniform_candidate_sampler(
true_classes=context_class,
num_true=1,
num_sampled=num_ns,
unique=True,
range_max=sku_num,
seed=SEED,
name="negative_sampling")
根據(jù)正樣本的 context_class 采樣 num_ns 個負樣本,詞庫大小由 sku 總數(shù)決定,unique 用于指定負采樣樣本是否可以重復(fù)。
- 構(gòu)建正負樣本與 Labels
# Build context and label vectors (for one target word)
negative_sampling_candidates = tf.expand_dims(negative_sampling_candidates, 1)
context = tf.concat([context_class, negative_sampling_candidates], 0)
label = tf.constant([1] + [0] * num_ns, dtype="int64")
# Append each element from the training example to global lists.
targets.append(target_word)
contexts.append(context)
labels.append(label)
一個正樣本與 num_ns 個負樣本,所以 labels 構(gòu)建采用 1 + 0 x ns 的構(gòu)造方式。
Tips:
這里第三種方法主要是介紹一種負樣本采樣方法,相比第二種方式其不夠簡潔,因此后續(xù)模型實戰(zhàn)中,我們的正負樣本與 Label 采用第二種 skipgram 的方法構(gòu)建。
六.總結(jié)
本文涉及到很多細節(jié)內(nèi)容并未完全展示,更詳細的講解可以參考:
- 根據(jù) networkx 獲取生成帶權(quán)圖:GraphEmbedding networks 獲取圖結(jié)構(gòu)
- DeepWalk 隨機游走:GraphEmbedding DeepWalk 圖文詳解
- Alias 采樣:GraphEmbedding Alias 采樣圖文詳解
- Node2vec 序列游走:GraphEmbedding Node2vec 圖文詳解
- EGES 算法分析:EGES 與推薦系統(tǒng)用戶冷啟動
- Parallel 多線程:Python - 多線程 Parallel / Multiprocessing 示例
- 更多推薦算法相關(guān)深度學習:深度學習導讀專欄?文章來源:http://www.zghlxwxcb.cn/news/detail-422226.html
最后感謝??https://github.com/wangzhegeek/EGES?項目的開發(fā)者,上述數(shù)據(jù)預(yù)處理的數(shù)據(jù)以及 utils 工具類大家可以在 Github 項目中獲取。本文主要對 DataProcess 的過程進行了圖文的詳細分析以供大家參考,后續(xù)將基于上述預(yù)處理得到的數(shù)據(jù)通過?Keras 自定義 Word2vec 與 EGES。文章來源地址http://www.zghlxwxcb.cn/news/detail-422226.html
到了這里,關(guān)于深度學習 - 40. N-Gram 采樣與 Session 數(shù)據(jù)獲取 For EGES的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!