本文主要記錄:
1. 離散特征如何預處理之后嵌入
2.使用pytorch怎么使用nn.embedding
以推薦系統中:考慮輸入樣本只有兩個特征,用邏輯回歸來預測點擊率ctr
看圖混個眼熟,后面再說明:
一、離散數據預處理
假設一個樣本有兩個離散特征【職業,省份】,第一個特征種類有10種,第二個特征種類有20種。於是field_dims=[10, 20]
“職業”的取值為:[學生,老師,老板,司機……]共10種
“省份”的取值為:[黑龍江、吉林、四川(第3個位置)、……、北京(第7個位置)、……、重慶(第19個位置)、……]
設原始數據部分樣本是這樣的(標簽沒寫出來,點擊就為1,未點擊就為0):
[學生,四川],[學生,北京],[老師,重慶]
那么處理過后變為
[1, 3], [1, 7], [2, 19]
假設我們在torch嵌入方式為:所有特征同時一起嵌入(還有就是分別嵌入:就是每個特征分開嵌入,然后把所有特征的嵌入向量用torch.cat拼接成一個向量):
field_dims=[10,20],取embed_dim=3
Self.embed = torch.nn.Embedding(sum(field_dims), embed_dim)
這個時候得到了一個索引字典index_dict,長度為sum(field_dims)=30, 每個索引對應一個嵌入維度為embed_dim=3的向量(相當於字典的key為1到30,每個key對應一個嵌入的維度為embed_dim=3的向量)
現在,我們該怎么把[學生,四川],對應的[1, 3]數據嵌入進去,嵌入的結果是什么呢?
這個時候需要把[1, 3], [1, 7], [2, 19]變為[1, 13], [1, 17], [2, 29].
因為在one-hot編碼的時候[1, 3]中的1對應“1,0,0,0,0,0,0,0,0,0”,3對應“0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0”,
最終形成的one-hot向量為“1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0”,1和3分別在第1位置和第13位置(我把這種操作簡單叫做:變換處理)。
設batch_size=2,那么在定義模型的時候,def forward(self, x)中的x的size就等於batch_size*特征數=2*2。
假如第一個batch讀取的數據是[1, 3], [1, 7], 則x=[[1,3], [1,7]],經過“變換處理”變為了x_=[[1,13], [1,17]],
這個時候就可以把x_輸入到嵌入層,embed_result = Self.embed(x_)
得到的結果embed_result的size為:batch_size*特征數*嵌入維度=2*2*3。
設1,13,17對應的嵌入向量分為為[0.1, 0.2, 0.3], [0.3, 0.6, 0.2], [0.3, 0.9, 0.2],
那么embed_result結果為(還是一個tensor,這兒只是展現了數據):
因為batch_size=2,是兩個樣本,所以最后需要得到兩個輸出。
R1 = torch.sum(embed_result,1) # size=batch_size*嵌入維度=2*3
[0.1+0.7, 0.2+0.6, 0.3+0.2] = [0.8, 0.8, 0.5]
R2 = torch.sum(R1,1) # size = batch_size=2
(注意,如果還要在添加層的話,可以把R1作為下一層的輸入,或者把embed_result每一個樣本的特征拼接起來得到R3輸入到下一層)
上面這個怎么和公式聯系起來呢,感覺直接是把特征嵌入過后的向量直接相加求和得到輸出y,公式前面的權重w怎么體現的呢?
因為特征x1有10個取值,x2有20個取值,one-hot編碼過后,特征變成了30個,設新的特征為f:
原來的式子相當於變成了:
對於樣本[學生,四川]—>[1,3]-變換處理->[1, 13],
只有x1,1一個為1,
其中也只有x2,3一個為1
然而實際上並沒有采用one-hot存儲,當用了embedding過后只是存儲了一個索引字典,設嵌入的為度為3,式子相當於(設θ0=0)
這就與R2中的結果對應上了。所以直接把樣本嵌入向量的元素值全部加起來,就是輸出。
圖解:
針對輸入為[學生,四川]-->[1,3]-->[1, 13],
輸入層one-hot編碼第1個位置和第13個位置是1(藍色),其余位置是0
(啟示程序中並不是輸入的one-hot編碼,只是一個索引key=1和13,通過索引去得到嵌入過后的向量)
1.嵌入
embed_result = Self.embed(x_)
2.每個特征的對應元素求和,得到embedding層第i個神經元的值
對應代碼R1 = torch.sum(embed_result,1)。# size=batch_size*嵌入維度=2*3
比如,以輸入[1,3]à[1,13]為例,
得到嵌入層三個神經元的值為:
同理計算得到[1,7]-->[1,17]對應的embedding層神經元的值
即:
3. 把每個樣本embeding層所有神經元的值加起來。
對應代碼R2 = torch.sum(R1, 1) # size = batch_size=2
補充說明
特征嵌入的兩種方式
1.所有特征一起嵌入
field_dims=[10,20], emb_dim1=3, 設batch_size=2x1=[1, 3],x2=[1,7],
於是一個batch中 x=[[1, 3], [1, 7]],
Pytorch代碼:
embed_1 = torch.nn.Embedding(sum(field_dims), emb_dim1)
x “變換處理”后,得到x_=[[1, 13], [1, 17]]
輸入x_,輸出embed_result1
embed_result1 = embed_1(x_)#size=batch_size*特征數*嵌入維度=2*2*3
此時需要用r1 = torch.sum(embed_result1, 1)得到的結果size=2*3
r2 = torch.sum(r1,1) # size = batch_size =2
ebd = nn.Embedding(30,3) x_ = Variable(torch.LongTensor([[1, 13], [1, 17]])) print(ebd(x_)) #2*2*3 r1 = torch.sum(ebd(x_),1) print(r1) # 2*3 r2 = torch.sum(r1,1) print(r2) #size = 2 輸出; tensor([[[ 2.2919, 0.2820, -0.0129], [-0.1115, 2.2074, 0.1836]], [[ 2.2919, 0.2820, -0.0129], [ 1.0564, 0.4073, -1.7239]]], grad_fn=<EmbeddingBackward>) tensor([[ 2.1804, 2.4893, 0.1707], [ 3.3483, 0.6892, -1.7368]], grad_fn=<SumBackward1>) tensor([4.8404, 2.3007], grad_fn=<SumBackward1>)
2.特征分別嵌入,然后再cat。
(比如,模型准備把嵌入維度設為100,同時嵌入的嵌入維度直接為100,分別嵌入各個特征的嵌入維度之和為100)
(同時嵌入的嵌入維度為3,為了保持一致,分別嵌入時候第一個特征嵌入維度設為1,第二個特征設嵌入維度為2)
field_dims=[10,20], emb_dim2_1=1, emb_dim2_1=2
embed2_1 = torch.nn.Embedding(filed_dims[0], emb_dim2_1)
embed2_2 = torch.nn.Embedding(filed_dims[1], emb_dim2_2)
embed_result2_1 = embed2_1(x_[ : ,0]) #batch_size*emb_dim2_1=2*1
#相當於得到對[1, 1]嵌入的結果
embed_result2_2 = embed2_2(x_[ : ,1]) #batch_size*emb_dim2_1=2*2
#相當於得到對[13, 17]嵌入的結果
embed_result_list = embed_result2_1+ embed_result2_2
embed_result2 = torch.cat(embed_result_list, 1) #size=batch_size*( emb_dim2_1+ emb_dim2_2=2*1*(1+2)=2*3
最后使用torch.sum(embed_result2, 1) # size=2
x_ = Variable(torch.LongTensor([[1, 13], [1, 17]])) embed2_1 = nn.Embedding(10,1) embed2_2 = nn.Embedding(20,2) embed_result2_1 = embed2_1(x_[ : ,0]) embed_result2_2 = embed2_2(x_[ : ,1]) print(embed_result2_1) # batch_size*emb_dim2_1=2*1 print(embed_result2_2) #batch_size*emb_dim2_1=2*2 lisi_ebd2 = [] lisi_ebd2.append(embed_result2_1) lisi_ebd2.append(embed_result2_2) # lisi_ebd2長度為2,一個元素為2*1大小,一個元素為2*2 embed_result2 = torch.cat(lisi_ebd2,1) print(embed_result2) #2*3 p = torch.sum(embed_result2, 1) print(p) # size=2 輸出: tensor([[0.4481], [0.4481]], grad_fn=<EmbeddingBackward>) tensor([[ 0.7232, 0.6618], [-1.4629, 1.1546]], grad_fn=<EmbeddingBackward>) tensor([[ 0.4481, 0.7232, 0.6618], [ 0.4481, -1.4629, 1.1546]], grad_fn=<CatBackward>) tensor([1.8331, 0.1397], grad_fn=<SumBackward1>)