DCN-Mix和DCN-V2的關系
DCN-Mix(a mixture of low-rank DCN)是基于DCN-V2的改進版,它提出使用矩陣分解來降低DCN-V2的時間空間復雜度,又引入多次矩陣分解來達到類似混合專家網絡MOE的效果從而提升交叉層的表征能力,若讀者對DCN-V2不甚了解可以參考上一節[特征交叉系列:Deep&Cross(DCN-V2)理論和實踐]做知識鋪墊。
DCN-V2權重矩陣的低秩性和矩陣分解
在DCN-V2中核心的參數是交叉層的權重矩陣W,該參數是M×M的方陣,其中M是所有輸入embedding拼接后的向量總長度,每一層交叉之間W不共享,W矩陣需要學習的參數數量能占到所有參數量的70%以上,而進一步作者發現隨著網絡的訓練,W矩陣的奇異值出現快速下降呈現出低秩特性,代表該矩陣存在信息冗余,因此可以考慮通過矩陣分解來進行特征提取和信息壓縮。
在PyTorch中可以通過torch.linalg.svd計算出矩陣的奇異值,例如
>>> a = torch.tensor([[1, 1], [1, 1.1]])
>>> u, s, v = torch.linalg.svd(a)
>>> print(s)
tensor([2.0512, 0.0488])
其中s是對角陣,斜對角線上的值就是奇異值,a矩陣的第二行幾乎可以從第一行線性變換而來,因此s各位置上的奇異值差距極大,第一個奇異值基本攜帶了全部的矩陣信息。
在DCN-V2的訓練代碼里面,打印出第一個交叉層初始化的W矩陣和訓練早停后W矩陣的奇異值,奇異值的長度和輸入長度M一致,代碼如下
# 初始化時
model = DCN(field_num=10, feat_dim=72, emb_num=16, order_num=2, dropout=0.1, method='parallel').to(DEVICE)
init_s = torch.linalg.svd(model.cross_net.cell_list[0].w)[1].cpu().detach().numpy().tolist()
# 早停時
if early_stop_flag:train_s = torch.linalg.svd(model.cross_net.cell_list[0].w)[1].cpu().detach().numpy().tolist()break
奇異值列表中元素大小逐個遞減,對init_s和train_s分別做最大最小歸一化,要求第一個奇異值歸因化為1,
init_s = [(x - min(init_s)) / (max(init_s) - min(init_s)) for x in init_s]
train_s = [(x - min(train_s)) / (max(train_s) - min(train_s)) for x in train_s]
然后做圖看一下初始矩陣的奇異值和收斂后的奇異值的各個位置元素的大小情況
import matplotlib.pylab as plt
plt.scatter(list(range(len(init_s))), init_s, label='init', s=3)
plt.scatter(list(range(len(train_s))), train_s, label='learned', s=3)
plt.legend(loc=0)
plt.show()
init和learned奇異值下降對比
相比于初始化階段(藍線),模型收斂后(橙線)的W矩陣奇異值急速下降,說明頭部的奇異值已經攜帶了大部分矩陣信息,W矩陣可以考慮做壓縮。
在論文中作者將W分解為U,V兩個矩陣的相乘,其中U,V都是維度為[M, R]的二維矩陣,M和輸入等長,R<=M/2,公式如下
矩陣分解
此時一個交叉權重的參數數量由M平方降低為2×MR。
DCN-Mix的混合專家網絡
DCN-Mix使用矩陣UV分解來逼近原始的交叉矩陣W,受到MOE(Mixture of Experts)混合專家網絡的啟發,作者對W進行多次矩陣分解,單個矩陣分解相當于單個專家網絡(Expert)在子空間學習特征交叉,再引入門控機制(Gate)對多個子空間的交叉結果進行自適應地融合,從而提高交叉層的表達能力,DCN結合MOE的示意圖如下
MOE示意圖
其中該層的輸入Input x分別進入n個Expert專家網絡,專家網絡中包含UV矩陣相乘,同時Input x輸入給一個門控網絡Gate+Softmax輸出n個權重標量,最后Input x會和加權求和的專家網絡結果做殘差連接。
將矩陣分解和MOE結合起來形成最終的交叉層公式如下
結合MOE的矩陣分解交叉層
相比于DCN-V2,等號左側的哈達瑪積部分改為了一個Σ加權求和的UV矩陣逼近,而右側的殘差連接放到最后和MOE的結果一起做殘差連接。
DCN-Mix在PyTorch下的實踐
本次實踐的數據集和上一篇特征交叉系列:完全理解FM因子分解機原理和代碼實戰一致,采用用戶的購買記錄流水作為訓練數據,用戶側特征是年齡,性別,會員年限等離散特征,商品側特征采用商品的二級類目,產地,品牌三個離散特征,隨機構造負樣本,一共有10個特征域,全部是離散特征,對于枚舉值過多的特征采用hash分箱,得到一共72個特征。
DCN-Mix的PyTorch代碼實現如下
class Embedding(nn.Module):def __init__(self, feat_num, emb_num):super(Embedding, self).__init__()self.embedding = nn.Embedding(feat_num, emb_num)nn.init.xavier_normal_(self.embedding.weight.data)def forward(self, x):# [None, filed_num] => [None, filed_num, emb_num] => [None, filed_num * emb_num]return self.embedding(x).flatten(1)class DNN(nn.Module):def __init__(self, input_num, hidden_nums, dropout=0.1):super(DNN, self).__init__()layers = []input_num = input_numfor hidden_num in hidden_nums:layers.append(nn.Linear(input_num, hidden_num))layers.append(nn.BatchNorm1d(hidden_num))layers.append(nn.ReLU())layers.append(nn.Dropout(p=dropout))input_num = hidden_numself.mlp = nn.Sequential(*layers)for layer in self.mlp:if isinstance(layer, nn.Linear):nn.init.xavier_normal_(layer.weight.data)def forward(self, x):return self.mlp(x)class CrossCell(nn.Module):"""一個交叉單元"""def __init__(self, input_num, r):super(CrossCell, self).__init__()self.v = nn.Parameter(torch.randn(input_num, r))self.u = nn.Parameter(torch.randn(input_num, r))self.b = nn.Parameter(torch.randn(input_num, 1))nn.init.xavier_normal_(self.v.data)nn.init.xavier_normal_(self.u.data)def forward(self, x0, xi):# [None, emb_num] => [None, emb_num, 1]xi = xi.unsqueeze(2)x0 = x0.unsqueeze(2)# [r, input_num] * [None, emb_num, 1] => [None, r, 1]# [input_num, r] * [None, r, 1] => [None, emb_num, 1]xii = (torch.matmul(self.u, torch.matmul(self.v.t(), xi)) + self.b) * x0return xii # [None, emb_num, 1]class MOECrossCell(nn.Module):def __init__(self, input_num, r, k):super(MOECrossCell, self).__init__()self.k = kself.cross_cell = nn.ModuleList([CrossCell(input_num, r) for i in range(self.k)])self.gate = nn.Linear(input_num, self.k)nn.init.xavier_normal_(self.gate.weight.data)def forward(self, x0, xi):# [None, emb_num] => [None, emb_num, 1]xii = xi.unsqueeze(2)export_out = []for i in range(self.k):cross_out = self.cross_cell[i](x0, xi)# [[None, emb_num, 1], [None, emb_num, 1], [None, emb_num, 1], [None, emb_num, 1]]export_out.append(cross_out)export_out = torch.concat(export_out, dim=2) # [None, emb_num, 4]# [None, k] => [None, 1, k]gate_out = self.gate(xi).softmax(dim=1).unsqueeze(dim=1)# [None, emb_num, 4] * [None, 1, k] = [None, emb_num, k] => [None, emb_num, 1]out = torch.sum(export_out * gate_out, dim=2, keepdim=True)out = out + xii # [None, emb_num, 1]return out.squeeze(2)class CrossNet(nn.Module):def __init__(self, order_num, input_num, r, k):super(CrossNet, self).__init__()self.order = order_numself.cell_list = nn.ModuleList([MOECrossCell(input_num, r, k) for i in range(order_num)])def forward(self, x0):xi = x0for i in range(self.order):xi = self.cell_list[i](x0=x0, xi=xi)return xiclass DCN(nn.Module):def __init__(self, field_num, feat_dim, emb_num, order_num, r=16, k=4, dropout=0.1, method='parallel',hidden_nums=(128, 64, 32)):super(DCN, self).__init__()input_num = field_num * emb_numself.embedding = Embedding(feat_num=feat_dim, emb_num=emb_num)self.dnn = DNN(input_num=input_num, hidden_nums=hidden_nums, dropout=dropout)self.cross_net = CrossNet(order_num=order_num, input_num=input_num, r=r, k=k)if method not in ('parallel', 'stacked'):raise ValueError('unknown combine type: ' + method)self.method = methodlinear_dim = hidden_nums[-1]if self.method == 'parallel':linear_dim = linear_dim + input_numself.linear = nn.Linear(linear_dim, 1)nn.init.xavier_normal_(self.linear.weight.data)def forward(self, x):emb = self.embedding(x) # [None, field * emb_num]cross_out = self.cross_net(emb) # [None, input_num]if self.method == 'parallel':dnn_out = self.dnn(emb) # [None, input_num]out = torch.concat([cross_out, dnn_out], dim=1)else:out = self.dnn(cross_out) # [None, input_num]out = self.linear(out)return torch.sigmoid(out).squeeze(dim=1)
在CrossCell模塊中完成了一個給予UV逼近的交叉操作,在MOECrossCell模塊中完成了MOE和殘差連接,其中export_out和gate_out分別為專家網絡的輸出和門控機制的權重。
本例全部是離散分箱變量,所有有值的特征都是1,因此只要輸入有值位置的索引即可,一條輸入例如
>>> train_data[0]
Out[120]: (tensor([ 2, 10, 14, 18, 34, 39, 47, 51, 58, 64]), tensor(0))
x的長度為10代表10個特征域,每個域的值是特征的全局位置索引,從0到71,一共72個特征。
DCN-Mix調參和效果對比
對階數(order_num)和融合策略(method)這兩個參數進行調參,分別嘗試1~4層交叉層,stacked和parallel兩種策略,采用10次驗證集AUC不上升作為早停條件,驗證集的平均AUC如下
DCN調參AUC | 并行parallel | 串行stacked |
---|---|---|
1層交叉(2階) | 0.6345 | 0.6321 |
2層交叉(3階) | 0.6328 | 0.6323 |
3層交叉(4階) | 0.6331 | 0.6333 |
4層交叉(5階) | 0.6340 | 0.6331 |
結論依舊是parallel效果好于stacked,其中一層交叉的并行parallel達到驗證集最優AUC為0.6345。
再對比一下之前文章中實踐的FM,FFM,PNN,DCN-V2等一系列算法,驗證集AUC和參數規模如下
算法 | AUC | 參數量 |
---|---|---|
FM | 0.6274 | 361 |
FFM | 0.6317 | 2953 |
PNN* | 0.6342 | 29953 |
DeepFM | 0.6322 | 12746 |
NFM | 0.6329 | 10186 |
DCN-parallel-3 | 0.6348 | 110017 |
DCN-stacked-3 | 0.6344 | 109857 |
DCN-Mix-parallel-1 | 0.6345 | 54501 |
DCN-Mix-stacked-3 | 0.6333 | 97869 |
使用矩陣分解逼近策略的DCN-Mix略低于原生的DCN-V2,但是還是超越一眾FM系列的算法,其中以同樣是三層交叉的stacked DCN為例,DCN-Mix的參數量相比于DCN-V2有所降低,也印證了論文中提到的“在模型效果和部署延遲之間找到一個平衡”。
最后的最后
感謝你們的閱讀和喜歡,我收藏了很多技術干貨,可以共享給喜歡我文章的朋友們,如果你肯花時間沉下心去學習,它們一定能幫到你。
因為這個行業不同于其他行業,知識體系實在是過于龐大,知識更新也非常快。作為一個普通人,無法全部學完,所以我們在提升技術的時候,首先需要明確一個目標,然后制定好完整的計劃,同時找到好的學習方法,這樣才能更快的提升自己。
這份完整版的大模型 AI 學習資料已經上傳CSDN,朋友們如果需要可以微信掃描下方CSDN官方認證二維碼免費領取【保證100%免費
】
一、全套AGI大模型學習路線
AI大模型時代的學習之旅:從基礎到前沿,掌握人工智能的核心技能!
二、640套AI大模型報告合集
這套包含640份報告的合集,涵蓋了AI大模型的理論研究、技術實現、行業應用等多個方面。無論您是科研人員、工程師,還是對AI大模型感興趣的愛好者,這套報告合集都將為您提供寶貴的信息和啟示。
三、AI大模型經典PDF籍
隨著人工智能技術的飛速發展,AI大模型已經成為了當今科技領域的一大熱點。這些大型預訓練模型,如GPT-3、BERT、XLNet等,以其強大的語言理解和生成能力,正在改變我們對人工智能的認識。 那以下這些PDF籍就是非常不錯的學習資源。
四、AI大模型商業化落地方案
五、面試資料
我們學習AI大模型必然是想找到高薪的工作,下面這些面試題都是總結當前最新、最熱、最高頻的面試題,并且每道題都有詳細的答案,面試前刷完這套面試題資料,小小offer,不在話下。
這份完整版的大模型 AI 學習資料已經上傳CSDN,朋友們如果需要可以微信掃描下方CSDN官方認證二維碼免費領取【保證100%免費
】