pytorch中,嵌入層torch.nn.embedding的計算方式


本文主要記錄:

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>)

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM