網站首頁 編程語言 正文
Wide&Deep Model、Wide Model(LR)、Deep Model、DeepFm Model、NFM Model復現(xiàn)筆記
作者:aaHua_ 更新時間: 2021-12-09 編程語言聲明:本模型復現(xiàn)筆記記錄自己學習過程,如果有錯誤請各位老師批評指正。
本周復現(xiàn)了Wide&Deep Model、Wide Model、Deep Model、DeepFm Model、NFM Model
dataset:cretio數據集(選取了train 20000, test 5000)
Wide&Deep Model結合了Wide Model(LR)的記憶性和Deep Model的泛化能力,對于部署應用來說會提高用戶的獲取率和滿意度,但是Wide&Deep的Wide part,需要專業(yè)的人員來發(fā)掘某些特征之間的共現(xiàn)關系,這就需要大量專業(yè)人工參與。
DeepFm Model 對Wide&Deep Model做了改進,對于Wide部分換成了FM Model具有了自動的特征組合能力。
NFM Model:是對FM、FFM的模型改進,F(xiàn)M、FFM其實就是一個二階的特征交叉,它們會受到組合爆炸問題,限制了FM、FFM的表達能力。NFM 就利用DNN去擬合FM的二階交叉特征。如果NFM一階部分是線性模型,也可以看成是Wide&Deep的改進,只不過在Deep部分加了特征交叉池化層。
Wide&Deep Model
Wide部分的輸入是原始特征(dense feature 和 經過Embeddings 的sparse feature)和交叉積特征轉換數據
交叉積特征轉換數據計算為:
Deep部分的輸入是dense feature 和 經過Embeddings 的sparse feature
class Linear(nn.Module):
def __init__(self, input_dim):
super(Linear, self).__init__()
self.Linear = nn.Linear(in_features = input_dim, out_features = 1)
def forward(self, x):
return self.Linear(x)
class Dnn(nn.Module):
def __init__(self, hidden_units, dropout = 0.):
super(Dnn, self).__init__()
self.dnn_network = nn.ModuleList([nn.Linear(layer[0],layer[1]) for layer in list(zip(hidden_units[:-1] , hidden_units[1:]))])
self.dropout = nn.Dropout(p = dropout)
def forward(self, x):
for linear in self.dnn_network:
x = linear(x)
x = self.dropout(x)
x = F.relu(x)
return x
class WideandDeep(nn.Module):
def __init__(self, feature_columns, hidden_units, dnn_dropout = 0.):
super(WideandDeep, self).__init__()
self.dense_feature_cols, self.sparse_feature_cols = feature_columns
#embedding
self.embed_layers = nn.ModuleDict({
'embed_' + str(i):nn.Embedding(num_embeddings = feat['feat_num'], embedding_dim = feat['embed_dim'])
for i ,feat in enumerate(self.sparse_feature_cols)
})
hidden_units.insert(0, len(self.dense_feature_cols) + len(self.sparse_feature_cols) * self.sparse_feature_cols[0]['embed_dim'])
self.dropout = nn.Dropout(p = dnn_dropout)
self.dnn_network = Dnn(hidden_units,dnn_dropout)
self.Linear = Linear(len(self.dense_feature_cols) + 1)
self.final_linear = nn.Linear(hidden_units[-1]+ 1, 1)
def forward(self, x):
dense_inputs , sparse_inputs = x[:,:len(self.dense_feature_cols)], x[:,len(self.dense_feature_cols):]
sparse_inputs = sparse_inputs.long()
sparse_embeds = [self.embed_layers['embed_'+str(i)](sparse_inputs[:,i]) for i in range(sparse_inputs.shape[1])]
sparse_embeds = torch.cat(sparse_embeds, axis = -1)
dnn_input = torch.cat([sparse_embeds, dense_inputs], axis=-1)
#n = 190000
#a = [print(sparse_inputs[:,i].max()) for i in range(sparse_inputs.shape[1])]
sparse_one_hot = [F.one_hot(sparse_inputs[:,i],sparse_inputs[:,i].max()+1) for i in range(sparse_inputs.shape[1]) ]
sparse_one_hot = torch.cat(sparse_one_hot,axis=-1)
sparse_one_hot = torch.tensor(sparse_one_hot)
#print(sparse_one_hot.shape)
sparse_one_hot = sparse_one_hot.reshape(dense_inputs.shape[0],-1)
c = torch.zeros(dense_inputs.shape[0] ,sparse_one_hot.shape[1] )
for i in range(0,dense_inputs.shape[0]):
c[i][1456]=1
c[i][16123]=1
cross = torch.prod(torch.pow(sparse_one_hot,c),axis = 1)
cross = cross.reshape(-1,1)
dense_input = torch.cat([dense_inputs,cross],axis=-1)
#wide
wide_out = self.Linear(dense_input) # (,14)
#print(wide_out.shape)
#deep
deep_out = self.dnn_network(dnn_input)#(,64)
#print(deep_out.shape)
output = torch.cat([wide_out, deep_out] ,axis = -1 )
outputs = F.sigmoid(self.final_linear(output))
return outputs
重點難點:Wide部分的交叉積特征轉換數據不容易復現(xiàn),我通過觀察數據,找到了一個組合特征。
這是實現(xiàn)交叉積特征轉換數據的主要代碼:
流程:所有sparse feature轉成one_hot編碼 ,設置C矩陣(函數中做指數),利用torch.pow和torch.prod完成函數運算,最后拼接到wide的輸入。
sparse_one_hot = [F.one_hot(sparse_inputs[:,i],sparse_inputs[:,i].max()+1) for i in range(sparse_inputs.shape[1]) ]
sparse_one_hot = torch.cat(sparse_one_hot,axis=-1)
sparse_one_hot = torch.tensor(sparse_one_hot)
#print(sparse_one_hot.shape)
sparse_one_hot = sparse_one_hot.reshape(dense_inputs.shape[0],-1)
c = torch.zeros(dense_inputs.shape[0] ,sparse_one_hot.shape[1] )
for i in range(0,dense_inputs.shape[0]):
c[i][1456]=1
c[i][16123]=1
cross = torch.prod(torch.pow(sparse_one_hot,c),axis = 1)
cross = cross.reshape(-1,1)
dense_input = torch.cat([dense_inputs,cross],axis=-1)
#wide
wide_out = self.Linear(dense_input) # (,14)
效果:batch_size= 256,數據小容易過擬合,加入了L2正則化(0.005),dropout(0.3)
Wide Mode
僅僅是LR,輸入數據是dense feature 和 經過embedding層的sparse feature
class Linear(nn.Module):
def __init__(self, feature_columns, dnn_dropout = 0.):
super(Linear, self).__init__()
self.dense_feature_cols,self.sparse_feature_cols = feature_columns
#embedding
self.embed_layers = nn.ModuleDict({
'embed_'+str(i):nn.Embedding(num_embeddings = feat['feat_num'], embedding_dim = feat["embed_dim"])
for i, feat in enumerate(self.sparse_feature_cols)
})
input_dim = len(self.dense_feature_cols) + len(self.sparse_feature_cols) * self.sparse_feature_cols[0]['embed_dim']
self.Linear = nn.Linear(in_features = input_dim, out_features = 1)
def forward(self, x):
dense_inputs ,sparse_inputs = x[:,:len(self.dense_feature_cols)],x[:,len(self.dense_feature_cols):]
sparse_inputs = sparse_inputs.long()
sparse_embeds = [self.embed_layers['embed_'+str(i)](sparse_inputs[:,i])
for i in range(sparse_inputs.shape[1])]
sparse_embeds = torch.cat(sparse_embeds, axis = -1)
wide_inputs = torch.cat([dense_inputs,sparse_embeds] , axis = -1)
outputs = self.Linear(wide_inputs)
return F.sigmoid(outputs)
效果: batch_size= 256,L2正則化(0.005),dropout(0.3)
Deep Model:
class Dnn(nn.Module):
def __init__(self, feature_columns, hidden_units, dnn_dropout = 0.):
super(Dnn, self).__init__()
self.dense_feature_cols,self.sparse_feature_cols = feature_columns
#embedding
self.embed_layers = nn.ModuleDict({
'embed_'+str(i):nn.Embedding(num_embeddings = feat['feat_num'], embedding_dim = feat["embed_dim"])
for i, feat in enumerate(self.sparse_feature_cols)
})
hidden_units.insert(0,len(self.dense_feature_cols) + len(self.sparse_feature_cols) * self.sparse_feature_cols[0]['embed_dim'])
self.dnn_network = nn.ModuleList([nn.Linear(layer[0],layer[1]) for layer in list(zip(hidden_units[:-1], hidden_units[1:]))])
self.dropout = nn.Dropout(p = dnn_dropout)
self.final_linear = nn.Linear(hidden_units[-1], 1 )
def forward(self, x):
dense_inputs ,sparse_inputs = x[:,:len(self.dense_feature_cols)],x[:,len(self.dense_feature_cols):]
sparse_inputs = sparse_inputs.long()
sparse_embeds = [self.embed_layers['embed_'+str(i)](sparse_inputs[:,i])
for i in range(sparse_inputs.shape[1])]
sparse_embeds = torch.cat(sparse_embeds, axis = -1)
dnn_inputs = torch.cat([dense_inputs,sparse_embeds] , axis = -1)
for layer in self.dnn_network:
dnn_inputs = layer(dnn_inputs)
dnn_inputs = F.relu(dnn_inputs)
output = F.sigmoid(self.final_linear(dnn_inputs))
return output
效果:batch_size= 256,L2正則化(0.005),dropout(0.3)
Wide&Deep Model VS Wide Model VS Deep Model
Wide&Deep Model 、 Wide Model 、 Deep Model 訓練損失比較
Wide&Deep Model 、 Wide Model 、 Deep Model 訓練AUC比較
Wide&Deep Model 、 Wide Model 、 Deep Model 測試集損失比較
Wide&Deep Model 、 Wide Model 、 Deep Model 測試集AUC比較
結論:Wide&Deep結合了記憶性和泛化性確實比樸素LR和樸素DNN效果好一點,論文中給出的三者的AUC幾乎一樣,文章中給出說明是利用cross-product feature去增加在線用戶獲取率,提高1%的效率對于大的互聯(lián)網公司來說就可以獲得巨大收益。
DeepFM
DeepFM對于Wide&Deep模型的改進是用FM替換了原來的Wide部分,加強了淺層網絡部分特征組合的能力。同時,F(xiàn)M部分和DNN部分共享相同的EMbedding層。
左側的FM部分對不同的特征域的EMbedding進行了兩兩交叉,即將Embedding向量當作原FM中的特征向量V。
class FM(nn.Module):
def __init__(self, latent_dim, fea_num):
super(FM, self).__init__()
self.latent_dim = latent_dim
self.w0 = nn.Parameter(torch.zeros([1,]))
self.w1 = nn.Parameter(torch.rand([fea_num, 1]))
self.w2 = nn.Parameter(torch.rand([fea_num, latent_dim]))
def forward(self, inputs):
# 一階
first_order = self.w0 + torch.mm(inputs, self.w1)
# 二階
second_order = 1/2 * torch.sum(torch.pow(torch.mm(inputs, self.w2), 2) - torch.mm(torch.pow(inputs,2), torch.pow(self.w2, 2)),dim = 1,keepdim = True)
return first_order + second_order
class Dnn(nn.Module):
def __init__(self, hidden_units, dropout=0.):
super(Dnn, self).__init__()
self.dnn_network = nn.ModuleList([nn.Linear(layer[0], layer[1]) for layer in list(zip(hidden_units[:-1], hidden_units[1:]))])
self.dropout = nn.Dropout(dropout)
def forward(self, x):
for linear in self.dnn_network:
x = linear(x)
x = F.relu(x)
x = self.dropout(x)
return x
class DeepFM(nn.Module):
def __init__(self, feature_columns, hidden_units, dnn_dropout=0.):
super(DeepFM, self).__init__()
self.dense_feature_cols, self.sparse_feature_cols = feature_columns
# embedding
self.embed_layers = nn.ModuleDict({
'embed_' + str(i): nn.Embedding(num_embeddings=feat['feat_num'], embedding_dim=feat['embed_dim'])
for i, feat in enumerate(self.sparse_feature_cols)
})
self.fea_num = len(self.dense_feature_cols) + len(self.sparse_feature_cols)*self.sparse_feature_cols[0]['embed_dim']
hidden_units.insert(0, self.fea_num)
self.fm = FM(self.sparse_feature_cols[0]['embed_dim'], self.fea_num)
self.dnn_network = Dnn(hidden_units, dnn_dropout)
self.nn_final_linear = nn.Linear(hidden_units[-1], 1)
def forward(self, x):
dense_inputs, sparse_inputs = x[:, :len(self.dense_feature_cols)], x[:, len(self.dense_feature_cols):]
sparse_inputs = sparse_inputs.long()
sparse_embeds = [self.embed_layers['embed_'+str(i)](sparse_inputs[:, i]) for i in range(sparse_inputs.shape[1])]
sparse_embeds = torch.cat(sparse_embeds, dim=-1)
x = torch.cat([sparse_embeds, dense_inputs], dim=-1)
# Wide
wide_outputs = self.fm(x)
# deep
deep_outputs = self.nn_final_linear(self.dnn_network(x))
outputs = F.sigmoid(torch.add(wide_outputs, deep_outputs))
return outputs
效果:batch_size= 256,L2正則化(0.006),dropout(0.4)
DeepFM VS Wide&Deep
結論: DeepFM具有自動進行特征組合的能力,效果還是比wide&deep效果要好。但是epoch=4前 wide&deep優(yōu)于DeepFM的原因:我認為是FM的隨機初始化導致的auc比wide&deep低。
NFM Model
NFM是對FM、FFM的模型改進,F(xiàn)M、FFM其實就是一個二階的特征交叉,它們會受到組合爆炸問題,限制了FM、FFM的表達能力。
之前看到過一句話“深度學習網絡理論上有擬合任何復雜函數的能力”,就是用DNN來擬合表達能力更強的函數。
NFM 就利用DNN去擬合FM的二階交叉特征。
如果NFM一階部分是線性模型,也可以看成是Wide&Deep的改進,只不過在Deep部分加了特征交叉池化層。
模型的其他層都與前面一樣含義,新添加的是Bi-interaction Pooling layer。
Bi-interaction Pooling layer:
其中前一層中的Embeding層Vx = [ x1v1,x2v2,…,xnvn ]
在進行兩兩Embedding向量的元素積操作后,對交叉特征向量取和,得到池化層的輸出向量。
然后再把該向量輸入到下一層的DNN中,進行進一步的交叉特征。
注意: 設計模型時不要忘了一階函數。
class Dnn(nn.Module):
def __init__(self, hidden_units, dropout=0.):
super(Dnn, self).__init__()
self.dnn_network = nn.ModuleList([nn.Linear(layer[0], layer[1]) for layer in list(zip(hidden_units[:-1], hidden_units[1:]))])
self.dropout = nn.Dropout(dropout)
def forward(self, x):
for linear in self.dnn_network:
x = linear(x)
x = F.relu(x)
x = self.dropout(x)
return x
class NFM(nn.Module):
def __init__(self, feature_columns, hidden_units, dnn_dropout=0.):
super(NFM, self).__init__()
self.dense_feature_cols, self.sparse_feature_cols = feature_columns
# embedding
self.embed_layers = nn.ModuleDict({
'embed_' + str(i): nn.Embedding(num_embeddings=feat['feat_num'], embedding_dim=feat['embed_dim'])
for i, feat in enumerate(self.sparse_feature_cols)
})
self.fea_num = len(self.dense_feature_cols) + self.sparse_feature_cols[0]['embed_dim']
hidden_units.insert(0, self.fea_num)
self.bn = nn.BatchNorm1d(self.fea_num)
self.dnn_network = Dnn(hidden_units, dnn_dropout)
input_dim = len(self.dense_feature_cols) + len(self.sparse_feature_cols) * self.sparse_feature_cols[0]['embed_dim']
self.Linear = nn.Linear(input_dim,32)
self.nn_final_linear = nn.Linear(hidden_units[-1], 1)
def forward(self, x):
dense_inputs, sparse_inputs = x[:, :len(self.dense_feature_cols)], x[:, len(self.dense_feature_cols):]
sparse_inputs = sparse_inputs.long()
sparse_embeds = [self.embed_layers['embed_'+str(i)](sparse_inputs[:, i]) for i in range(sparse_inputs.shape[1])]
L_sparse_embeds = torch.cat( sparse_embeds,axis = -1)
sparse_embeds = torch.stack(sparse_embeds)
sparse_embeds = sparse_embeds.permute((1, 0, 2))
embed_cross = 1/2 * (
torch.pow(torch.sum(sparse_embeds, dim=1),2) - torch.sum(torch.pow(sparse_embeds, 2), dim=1)
)
#wide
lr_input = torch.cat([dense_inputs,L_sparse_embeds],axis = -1)
lr_output = self.Linear(lr_input)
#print(lr_output.shape)
# deep
x = torch.cat([embed_cross, dense_inputs], dim=-1)
x = self.bn(x)
dnn_outputs = self.nn_final_linear(self.dnn_network(x)+ lr_output)
#joint
outputs = F.sigmoid(dnn_outputs )
return outputs
效果:batch_size= 256,L2正則化(0.003),dropout(0.25)
NFM VS DeepFM VS Wide&Deep
Wide&Deep、DeepFM模型是使用的拼接操作,而不是Bi-Interaction.
拼接操作最大的缺點是他并沒有考慮任何特征組合信息,全部依靠后面的DNN去學習特征組合,但是DNN的學習優(yōu)化非常困難。
使用Bi-Interaction就考慮到了二階組合特征,使得輸入到DNN層中數據包含更多的信息,減輕了DNN的學習困難。
原文鏈接:https://blog.csdn.net/weixin_43667611/article/details/122122478
相關推薦
- 2022-01-16 npm:使用npm link來調試本地的包
- 2021-12-24 基于PostgreSQL/openGauss?的分布式數據庫解決方案_PostgreSQL
- 2022-03-28 Python?pandas之多級索引取值詳解_python
- 2022-04-12 Windows11右鍵菜單恢復Windows10樣式
- 2022-06-28 C#基于自定義事件EventArgs實現(xiàn)發(fā)布訂閱模式_C#教程
- 2022-12-15 裝Oracle用PLSQL連接登錄時不顯示數據庫的解決_oracle
- 2022-06-17 C語言詳解函數與指針的使用_C 語言
- 2022-07-25 Python實現(xiàn)文件及文件夾操作大全_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支