推薦系統(二十二):基于MaskNet和WideDeep的商品推薦CTR模型實現

在上一篇文章《推薦系統(二十一):基于MaskNet的商品推薦CTR模型實現》中,筆者基于 MaskNet 構建了一個簡單的模型。筆者所經歷的工業級實踐證明,將 MaskNet 和 Wide&Deep 結合應用,可以取得不錯的效果,本文將基于MaskNet 和 Wide&Deep 實現一個商品推薦 CTR 模型。

1.MaskNet 與 Wide&Deep 結合的推薦系統優勢

MaskNet 與 Wide&Deep 結合,本質上是通過 “顯式記憶+隱式泛化+動態聚焦” 三重機制,實現從數據到場景的全鏈路優化。適用于電商、內容平臺等需平衡長期偏好與實時反饋、處理高稀疏特征的復雜推薦系統。關于兩者結合的優勢,可以從如下五個方面來看:

(1)結構互補,能力增強

  • Wide 部分:通過線性模型(如邏輯回歸)捕捉顯式特征交互(如用戶歷史點擊與商品的共現),擅長“記憶”(Memorization),解決高頻、確定性特征組合的推薦問題。
  • Deep 部分:通過多層神經網絡學習隱式特征交互,實現“泛化”(Generalization),挖掘長尾、稀疏特征之間的潛在關聯。
  • MaskNet:通過自注意力(Self-Attention)或門控機制動態調整特征權重,過濾噪聲并突出關鍵特征交互。顯式建模高階特征交互,減少人工特征工程依賴。動態特征權重調整提升模型對用戶實時興趣的捕捉能力。在實踐中,可將 MaskNet 作為 Deep 網絡的子模塊(如替換全連接層),通過自注意力機制增強隱式特征交互的建模能力。

(2)動態與靜態興趣融合

在用戶行為序列中,MaskNet 可動態放大近期點擊商品的影響力(如用戶臨時興趣),而 Wide 部分維持長期偏好(如用戶性別、年齡相關特征)。技術實現:通過門控機制(如 Sigmoid)生成特征掩碼,抑制噪聲特征(如偶然點擊),增強關鍵信號。

(3)稀疏數據與冷啟動優化

Wide 側人工交叉特征直接建模關鍵稀疏信號(如冷門品類偏好),MaskNet 利用注意力挖掘潛在關聯(如長尾商品相似性),緩解數據稀疏性,加速冷啟動收斂。

(4)高效工業落地

Wide 部分輕量級計算保障實時性,MaskNet 通過并行化設計(如 Multi-Head Attention)平衡效果與效率,適配高并發場景,支持多模態擴展(文本/圖像融合)。

(5)多目標協同優化

結合 Wide&Deep 的多任務學習能力(如 CTR 與 CVR 聯合預估)與 MaskNet 的靈活結構,聯合建模點擊率(CTR)、轉化率(CVR)等任務,兼顧推薦準確性、多樣性及業務指標(如 GMV),滿足復雜業務需求。


2. 模型架構

如下圖所示,在 Deep 部分增加了一個 MaskBlock,采用 Serial 模式。

  • Deep Feature:Deep Feature 包括用戶特征和商品特征,經過 Embedding Layer 并連接后維數(dimension)為 26。
  • Mask Block:包含兩個全連接層,輸出層(V_mask)維數必須為 26,從而與 Deep Feature 的 Embedding 結果 V_emb 的維數相同,如此才能進行點積計算。
  • MLP:V_mask 與 V_emb 進行點積計算后得到一個相等維數(dim=26)的加權向量 V_maskEMB。V_maskEMB 輸入 MLP(輸出層維數 32),輸出一個32維的 Deep Vector。
  • Wide Feature:Wide Feature 包括用戶和商品的多個交叉特征,如 user_id_x_item_id、user_gender_x_item_category,經過 Embedding Layer 并連接后維數(dimension)為 12000,正如其名,很“Wide”。
  • Linear Layer:Wide 部分的核心作用是捕捉簡單的線性關系(如交叉特征、記憶效應),其輸出應為一個標量值(即1維)。該標量會與 Deep 部分的輸出拼接,最終通過 Sigmoid 函數生成預測概率。在提出 Wide & Deep 模型的原始論文中,Wide 部分直接使用邏輯回歸,相當于Dense(1),本文的結構完全遵循了這一范式。
  • Deep 部分和 Wide 部分拼接:Deep Vector(dim=32),Wide Vector(dim=1),拼接后得到一個 32+1 =33 維的向量。
  • 預測輸出:輸出層只有一個神經元,33 維的向量加權求和得到一個 logits,經過 Sigmolid 函數得到 0~1的數值,這個數值即為 CTR Prediction。
  • 損失計算:CTR Prediction 與 Label 計算交叉墑損失。

在這里插入圖片描述


3. 模型實現

3.1 模擬數據生成

# ====================
# 1. 模擬數據生成
# ====================
def generate_mock_data(num_users=100, num_items=200, num_interactions=1000):"""生成模擬用戶、商品及交互數據"""# 設置隨機種子保證可復現性np.random.seed(42)tf.random.set_seed(42)# 用戶特征user_data = {'user_id': np.arange(1, num_users + 1),'user_age': np.random.randint(18, 65, size=num_users),'user_gender': np.random.choice(['male', 'female'], size=num_users),'user_occupation': np.random.choice(['student', 'worker', 'teacher'], size=num_users),'city_code': np.random.randint(1, 2856, size=num_users),'device_type': np.random.randint(0, 5, size=num_users)}# 商品特征item_data = {'item_id': np.arange(1, num_items + 1),'item_category': np.random.choice(['electronics', 'books', 'clothing'], size=num_items),'item_brand': np.random.choice(['brandA', 'brandB', 'brandC'], size=num_items),'item_price': np.random.randint(1, 199, size=num_items)}# 交互數據interactions = []for _ in range(num_interactions):user_id = np.random.randint(1, num_users + 1)item_id = np.random.randint(1, num_items + 1)# 點擊標簽。0: 未點擊, 1: 點擊。在真實場景中可通過客戶端埋點上報獲得用戶的點擊行為數據click_label = np.random.randint(0, 2)interactions.append([user_id, item_id, click_label])return user_data, item_data, interactions# 生成數據
num_users = 100
num_items = 200
user_data, item_data, interactions = generate_mock_data(num_users, num_items, 10000)

3.2 合并、劃分數據集

# ====================
# 2. 合并、劃分數據集
# ====================
# 合并用戶特征、商品特征和交互數據
interaction_df = pd.DataFrame(interactions, columns=['user_id', 'item_id', 'click_label'])
user_df = pd.DataFrame(user_data)
item_df = pd.DataFrame(item_data)
df = interaction_df.merge(user_df, on='user_id').merge(item_df, on='item_id')
# 劃分數據集:訓練集、測試集
labels = df[['click_label']]
features = df.drop(['click_label'], axis=1)
train_features, test_features, train_labels, test_labels = train_test_split(features, labels, test_size=0.2,random_state=42)

3.3 特征工程

# ====================
# 3. 特征工程
#
# 本部分對原始用戶數據、商品數據、用戶-商品交互數據進行分類處理,加工為模型訓練需要的特征
#  1.數值型特征:如用戶年齡、價格,少數場景下可直接使用,但最好進行標準化,從而消除量綱差異
#  2.類別型特征:需要進行 Embedding 處理
#  3.交叉特征:由于維度高,需要哈希技巧處理高維組合特征
# ====================
""" 
用戶特征處理
"""
user_id = feature_column.categorical_column_with_identity('user_id', num_buckets=num_users + 1)
user_id_emb = feature_column.embedding_column(user_id, dimension=8)scaler_age = StandardScaler()
df['user_age'] = scaler_age.fit_transform(df[['user_age']])
user_age = feature_column.numeric_column('user_age')user_gender = feature_column.categorical_column_with_vocabulary_list('user_gender', ['male', 'female'])
user_gender_emb = feature_column.embedding_column(user_gender, dimension=2)user_occupation = feature_column.categorical_column_with_vocabulary_list('user_occupation',['student', 'worker', 'teacher'])
user_occupation_emb = feature_column.embedding_column(user_occupation, dimension=2)city_code_column = feature_column.categorical_column_with_identity(key='city_code', num_buckets=2856)
city_code_emb = feature_column.embedding_column(city_code_column, dimension=8)device_types_column = feature_column.categorical_column_with_identity(key='device_type', num_buckets=5)
device_types_emb = feature_column.embedding_column(device_types_column, dimension=8)""" 
商品特征處理
"""
item_id = feature_column.categorical_column_with_identity('item_id', num_buckets=num_items + 1)
item_id_emb = feature_column.embedding_column(item_id, dimension=8)scaler_price = StandardScaler()
df['item_price'] = scaler_price.fit_transform(df[['item_price']])
item_price = feature_column.numeric_column('item_price')item_category = feature_column.categorical_column_with_vocabulary_list('item_category',['electronics', 'books', 'clothing'])
item_category_emb = feature_column.embedding_column(item_category, dimension=2)item_brand = feature_column.categorical_column_with_vocabulary_list('item_brand', ['brandA', 'brandB', 'brandC'])
item_brand_emb = feature_column.embedding_column(item_brand, dimension=2)""" 
交叉特征預處理 
"""
# 使用TensorFlow的交叉特征(crossed_column)定義了Wide部分的特征列,主要用于捕捉用戶與商品特征之間的組合效應
# 將用戶ID(user_id)和商品ID(item_id)組合成一個新特征,捕捉**“特定用戶對特定商品的偏好”**
# 用戶ID和商品ID的組合往往非常大(num_users * num_items),直接編碼會導致維度爆炸,使用哈希函數將組合映射
# 到固定數量的桶(如hash_bucket_size=10,000個),可控制內存和計算開銷,適用于稀疏高維特征(如用戶-商品對)
# crossed_column:生成交叉特征,以 user_id 和 item_id 為例,通過哈希函數將任意組合映射到固定范圍 [0, hash_bucket_size-1]
# 交叉過程:將 user_id 和 item_id 拼接為字符串,如"123_456",再計算哈希值 hash("123_456") % 10000 → 42,
# indicator_column:將哈希值轉換為稀疏One-Hot向量,維度為 hash_bucket_size。比如哈希值 42 將生成一個10000維向量,第42位為1,其余為0
user_id_x_item_id = feature_column.crossed_column([user_id, item_id], hash_bucket_size=10000)
user_id_x_item_id = feature_column.indicator_column(user_id_x_item_id)
user_gender_x_item_category = feature_column.crossed_column([user_gender, item_category], hash_bucket_size=1000)
user_gender_x_item_category = feature_column.indicator_column(user_gender_x_item_category)
user_occupation_x_item_brand = feature_column.crossed_column([user_occupation, item_brand], hash_bucket_size=1000)
user_occupation_x_item_brand = feature_column.indicator_column(user_occupation_x_item_brand)""" 
特征列定義 
"""
deep_feature_columns = [user_id_emb,user_age,user_gender_emb,user_occupation_emb,item_id_emb,item_category_emb,item_brand_emb,item_price
]wide_feature_columns = [user_id_x_item_id,user_gender_x_item_category,user_occupation_x_item_brand
]

3.4 構建 MaskWideDeep 模型

# ====================
# 4. 構建MaskWideDeep模型
# MaskNet 是一個結合動態特征加權的深度模型,適用于結構化數據的二分類任務。
# 其核心創新點是通過子網絡生成樣本級別的特征權重,增強了模型的特征選擇能力,結構簡單但有效
# ====================
class MaskWideDeep(tf.keras.Model):"""初始化函數中進行特征列分離:即通過deep_feature_columns和wide_feature_columns明確定義了哪些特征屬于Deep部分(嵌入特征、數值特征),哪些屬于Wide部分(交叉特征)"""def __init__(self, deep_feature, wide_feature, hidden_units=[64, 32]):super().__init__()# 定義DenseFeatures層,處理特征列# 作用:所有類型的特征列(包括數值列、分桶列、嵌入列等)必須通過DenseFeatures轉換為密集張量(Dense Tensor),才能作為神經網絡的輸入# 原因:# 1-feature_column 只是定義了特征的處理邏輯(如分桶、哈希、嵌入等),并不實際執行數據轉換,因此 deep_feature 和 wide_feature 并不是張量。# 換言之,特征列(如 numeric_column, embedding_column)僅定義了如何從輸入數據中提取特征,但未執行實際轉換,例如 numeric_column 定義了數值特征的標準化邏輯# 2-DenseFeatures 層的作用是將原始輸入數據(字典形式)根據特征列的定義,轉換為模型可用的多維密集張量self.deep_dense_features = tf.keras.layers.DenseFeatures(deep_feature)self.wide_dense_features = tf.keras.layers.DenseFeatures(wide_feature)# Wide部分(線性模型)self.linear = tf.keras.layers.Dense(1, activation=None)# Deep部分# MaskBlock組件# mask_block 的輸出維度需嚴格匹配特征列處理后的維度(如 26),否則矩陣相乘會失敗self.mask_block = tf.keras.Sequential([tf.keras.layers.Dense(128, activation='relu'),# 這一層的輸出維數必須與 deep_feature 的維數相同,否則動態加權計算時(mask*feature_emb)會報錯# Sigmoid 激活確保權重在[0, 1] 之間,實現特征重要性軟選擇。類似注意力機制,抑制不重要特征,增強關鍵特征tf.keras.layers.Dense(26, activation='sigmoid')  # 生成動態權重])# 深度網絡:由多個全連接層組成,默認結構為 [64, 32],激活函數為 ReLU# 作用:對加權后的特征進行高階非線性組合學習self.dnn = tf.keras.Sequential([tf.keras.layers.Dense(unit, activation='relu')for unit in hidden_units])# 輸出層:單個神經元,Sigmoid 激活,適用于二分類任務,輸出概率值self.output_layer = tf.keras.layers.Dense(1, activation='sigmoid')# 單一輸入參數:inputs 參數是一個字典,包含所有原始特征(如user_id、user_age等)。# 特征自動篩選:每個 DenseFeatures 層會根據綁定的特征列定義,從inputs中提取對應的特征并處理# 例如,wide_dense_features 會提取 user_id_x_item_id 等交叉特征,而忽略其他特征def call(self, inputs):# wide 部分,與普通Wide&Deep模型相同wide_in = self.wide_dense_features(inputs)wide_out = self.linear(wide_in)# 特征嵌入:輸入數據轉換為密集張量 x# 輸入層:使用 DenseFeatures 處理結構化數據的特征列(feature_columns),將原始特征(如數值型、分類型)轉換為密集張量deep_in = self.deep_dense_features(inputs)# 動態加權:mask_block 生成掩碼 mask,與 deep_in 逐元素相乘(deep_in * mask),實現特征動態選擇mask = self.mask_block(deep_in)deep_in = tf.keras.layers.multiply([deep_in, mask])  # 特征動態加權# 特征學習:加權后的特征輸入 dnn 進行深度特征提取deep_out = self.dnn(deep_in)# Wide部分的線性層和Deep部分的DNN層獨立訓練,通過concatenate([wide_out, deep_out])實現聯合預測# 預測輸出:最終通過 Sigmoid 輸出概率combined = tf.keras.layers.concatenate([wide_out, deep_out])return self.output_layer(combined)

3.5 模型訓練、評估、保存

# ====================
# 5. 模型訓練、評估、保存
# ====================
# 創建輸入函數
def df_to_dataset(features, labels, shuffle=True, batch_size=32):ds = tf.data.Dataset.from_tensor_slices((dict(features),{'output_1': labels['click_label']}))if shuffle:ds = ds.shuffle(1000)ds = ds.batch(batch_size)return ds# 轉換數據集
train_ds = df_to_dataset(train_features, train_labels)
test_ds = df_to_dataset(test_features, test_labels, shuffle=False)# 初始化模型
model = MaskWideDeep(deep_feature_columns, wide_feature_columns)
# 編譯
# loss='binary_crossentropy':使用二元交叉熵損失函數,因為 CTR 預估這是一個二分類問題
# metrics=['AUC', 'accuracy']:在訓練和評估時跟蹤兩個指標
model.compile(optimizer='adam',loss='binary_crossentropy',metrics=['AUC', 'accuracy'])# 訓練
history = model.fit(train_ds,validation_data=test_ds,epochs=10,verbose=1)# 評估
test_loss, test_auc, test_acc = model.evaluate(test_ds)
print(f"\n測試集評估結果:AUC={test_auc:.3f}, 準確率={test_acc:.3f}")# 示例:繪制訓練曲線
import matplotlib.pyplot as pltplt.plot(history.history['accuracy'], label='Train Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.plot(history.history['loss'], label='Loss')plt.legend()
plt.show()# ====================
# 6. 模型保存
# ====================
model.save('masknet_recommender')
print("模型已保存到 masknet_recommender 目錄")

3.6 完整代碼

import tensorflow as tf# MAC M1 芯片不支持部分命令,因此禁用GPU設備
tf.config.set_visible_devices([], 'GPU')  # 禁用GPU設備
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from tensorflow import feature_column# ====================
# 1. 模擬數據生成
# ====================
def generate_mock_data(num_users=100, num_items=200, num_interactions=1000):"""生成模擬用戶、商品及交互數據"""# 設置隨機種子保證可復現性np.random.seed(42)tf.random.set_seed(42)# 用戶特征user_data = {'user_id': np.arange(1, num_users + 1),'user_age': np.random.randint(18, 65, size=num_users),'user_gender': np.random.choice(['male', 'female'], size=num_users),'user_occupation': np.random.choice(['student', 'worker', 'teacher'], size=num_users),'city_code': np.random.randint(1, 2856, size=num_users),'device_type': np.random.randint(0, 5, size=num_users)}# 商品特征item_data = {'item_id': np.arange(1, num_items + 1),'item_category': np.random.choice(['electronics', 'books', 'clothing'], size=num_items),'item_brand': np.random.choice(['brandA', 'brandB', 'brandC'], size=num_items),'item_price': np.random.randint(1, 199, size=num_items)}# 交互數據interactions = []for _ in range(num_interactions):user_id = np.random.randint(1, num_users + 1)item_id = np.random.randint(1, num_items + 1)# 點擊標簽。0: 未點擊, 1: 點擊。在真實場景中可通過客戶端埋點上報獲得用戶的點擊行為數據click_label = np.random.randint(0, 2)interactions.append([user_id, item_id, click_label])return user_data, item_data, interactions# 生成數據
num_users = 100
num_items = 200
user_data, item_data, interactions = generate_mock_data(num_users, num_items, 10000)# ====================
# 2. 合并、劃分數據集
# ====================
# 合并用戶特征、商品特征和交互數據
interaction_df = pd.DataFrame(interactions, columns=['user_id', 'item_id', 'click_label'])
user_df = pd.DataFrame(user_data)
item_df = pd.DataFrame(item_data)
df = interaction_df.merge(user_df, on='user_id').merge(item_df, on='item_id')
# 劃分數據集:訓練集、測試集
labels = df[['click_label']]
features = df.drop(['click_label'], axis=1)
train_features, test_features, train_labels, test_labels = train_test_split(features, labels, test_size=0.2,random_state=42)# ====================
# 3. 特征工程
#
# 本部分對原始用戶數據、商品數據、用戶-商品交互數據進行分類處理,加工為模型訓練需要的特征
#  1.數值型特征:如用戶年齡、價格,少數場景下可直接使用,但最好進行標準化,從而消除量綱差異
#  2.類別型特征:需要進行 Embedding 處理
#  3.交叉特征:由于維度高,需要哈希技巧處理高維組合特征
# ====================
""" 
用戶特征處理
"""
user_id = feature_column.categorical_column_with_identity('user_id', num_buckets=num_users + 1)
user_id_emb = feature_column.embedding_column(user_id, dimension=8)scaler_age = StandardScaler()
df['user_age'] = scaler_age.fit_transform(df[['user_age']])
user_age = feature_column.numeric_column('user_age')user_gender = feature_column.categorical_column_with_vocabulary_list('user_gender', ['male', 'female'])
user_gender_emb = feature_column.embedding_column(user_gender, dimension=2)user_occupation = feature_column.categorical_column_with_vocabulary_list('user_occupation',['student', 'worker', 'teacher'])
user_occupation_emb = feature_column.embedding_column(user_occupation, dimension=2)city_code_column = feature_column.categorical_column_with_identity(key='city_code', num_buckets=2856)
city_code_emb = feature_column.embedding_column(city_code_column, dimension=8)device_types_column = feature_column.categorical_column_with_identity(key='device_type', num_buckets=5)
device_types_emb = feature_column.embedding_column(device_types_column, dimension=8)""" 
商品特征處理
"""
item_id = feature_column.categorical_column_with_identity('item_id', num_buckets=num_items + 1)
item_id_emb = feature_column.embedding_column(item_id, dimension=8)scaler_price = StandardScaler()
df['item_price'] = scaler_price.fit_transform(df[['item_price']])
item_price = feature_column.numeric_column('item_price')item_category = feature_column.categorical_column_with_vocabulary_list('item_category',['electronics', 'books', 'clothing'])
item_category_emb = feature_column.embedding_column(item_category, dimension=2)item_brand = feature_column.categorical_column_with_vocabulary_list('item_brand', ['brandA', 'brandB', 'brandC'])
item_brand_emb = feature_column.embedding_column(item_brand, dimension=2)""" 
交叉特征預處理 
"""
# 使用TensorFlow的交叉特征(crossed_column)定義了Wide部分的特征列,主要用于捕捉用戶與商品特征之間的組合效應
# 將用戶ID(user_id)和商品ID(item_id)組合成一個新特征,捕捉**“特定用戶對特定商品的偏好”**
# 用戶ID和商品ID的組合往往非常大(num_users * num_items),直接編碼會導致維度爆炸,使用哈希函數將組合映射
# 到固定數量的桶(如hash_bucket_size=10,000個),可控制內存和計算開銷,適用于稀疏高維特征(如用戶-商品對)
# crossed_column:生成交叉特征,以 user_id 和 item_id 為例,通過哈希函數將任意組合映射到固定范圍 [0, hash_bucket_size-1]
# 交叉過程:將 user_id 和 item_id 拼接為字符串,如"123_456",再計算哈希值 hash("123_456") % 10000 → 42,
# indicator_column:將哈希值轉換為稀疏One-Hot向量,維度為 hash_bucket_size。比如哈希值 42 將生成一個10000維向量,第42位為1,其余為0
user_id_x_item_id = feature_column.crossed_column([user_id, item_id], hash_bucket_size=10000)
user_id_x_item_id = feature_column.indicator_column(user_id_x_item_id)
user_gender_x_item_category = feature_column.crossed_column([user_gender, item_category], hash_bucket_size=1000)
user_gender_x_item_category = feature_column.indicator_column(user_gender_x_item_category)
user_occupation_x_item_brand = feature_column.crossed_column([user_occupation, item_brand], hash_bucket_size=1000)
user_occupation_x_item_brand = feature_column.indicator_column(user_occupation_x_item_brand)""" 
特征列定義 
"""
deep_feature_columns = [user_id_emb,user_age,user_gender_emb,user_occupation_emb,item_id_emb,item_category_emb,item_brand_emb,item_price
]wide_feature_columns = [user_id_x_item_id,user_gender_x_item_category,user_occupation_x_item_brand
]# ====================
# 4. 構建MaskWideDeep模型
# MaskNet 是一個結合動態特征加權的深度模型,適用于結構化數據的二分類任務。
# 其核心創新點是通過子網絡生成樣本級別的特征權重,增強了模型的特征選擇能力,結構簡單但有效
# ====================
class MaskWideDeep(tf.keras.Model):"""初始化函數中進行特征列分離:即通過deep_feature_columns和wide_feature_columns明確定義了哪些特征屬于Deep部分(嵌入特征、數值特征),哪些屬于Wide部分(交叉特征)"""def __init__(self, deep_feature, wide_feature, hidden_units=[64, 32]):super().__init__()# 定義DenseFeatures層,處理特征列# 作用:所有類型的特征列(包括數值列、分桶列、嵌入列等)必須通過DenseFeatures轉換為密集張量(Dense Tensor),才能作為神經網絡的輸入# 原因:# 1-feature_column 只是定義了特征的處理邏輯(如分桶、哈希、嵌入等),并不實際執行數據轉換,因此 deep_feature 和 wide_feature 并不是張量。# 換言之,特征列(如 numeric_column, embedding_column)僅定義了如何從輸入數據中提取特征,但未執行實際轉換,例如 numeric_column 定義了數值特征的標準化邏輯# 2-DenseFeatures 層的作用是將原始輸入數據(字典形式)根據特征列的定義,轉換為模型可用的多維密集張量self.deep_dense_features = tf.keras.layers.DenseFeatures(deep_feature)self.wide_dense_features = tf.keras.layers.DenseFeatures(wide_feature)# Wide部分(線性模型)self.linear = tf.keras.layers.Dense(1, activation=None)# Deep部分# MaskBlock組件# mask_block 的輸出維度需嚴格匹配特征列處理后的維度(如 26),否則矩陣相乘會失敗self.mask_block = tf.keras.Sequential([tf.keras.layers.Dense(128, activation='relu'),# 這一層的輸出維數必須與 deep_feature 的維數相同,否則動態加權計算時(mask*feature_emb)會報錯# Sigmoid 激活確保權重在[0, 1] 之間,實現特征重要性軟選擇。類似注意力機制,抑制不重要特征,增強關鍵特征tf.keras.layers.Dense(26, activation='sigmoid')  # 生成動態權重])# 深度網絡:由多個全連接層組成,默認結構為 [64, 32],激活函數為 ReLU# 作用:對加權后的特征進行高階非線性組合學習self.dnn = tf.keras.Sequential([tf.keras.layers.Dense(unit, activation='relu')for unit in hidden_units])# 輸出層:單個神經元,Sigmoid 激活,適用于二分類任務,輸出概率值self.output_layer = tf.keras.layers.Dense(1, activation='sigmoid')# 單一輸入參數:inputs 參數是一個字典,包含所有原始特征(如user_id、user_age等)。# 特征自動篩選:每個 DenseFeatures 層會根據綁定的特征列定義,從inputs中提取對應的特征并處理# 例如,wide_dense_features 會提取 user_id_x_item_id 等交叉特征,而忽略其他特征def call(self, inputs):# wide 部分,與普通Wide&Deep模型相同wide_in = self.wide_dense_features(inputs)wide_out = self.linear(wide_in)# 特征嵌入:輸入數據轉換為密集張量 x# 輸入層:使用 DenseFeatures 處理結構化數據的特征列(feature_columns),將原始特征(如數值型、分類型)轉換為密集張量deep_in = self.deep_dense_features(inputs)# 動態加權:mask_block 生成掩碼 mask,與 deep_in 逐元素相乘(deep_in * mask),實現特征動態選擇mask = self.mask_block(deep_in)deep_in = tf.keras.layers.multiply([deep_in, mask])  # 特征動態加權# 特征學習:加權后的特征輸入 dnn 進行深度特征提取deep_out = self.dnn(deep_in)# Wide部分的線性層和Deep部分的DNN層獨立訓練,通過concatenate([wide_out, deep_out])實現聯合預測# 預測輸出:最終通過 Sigmoid 輸出概率combined = tf.keras.layers.concatenate([wide_out, deep_out])return self.output_layer(combined)# ====================
# 5. 模型訓練與評估
# ====================
# 創建輸入函數
def df_to_dataset(features, labels, shuffle=True, batch_size=32):ds = tf.data.Dataset.from_tensor_slices((dict(features),{'output_1': labels['click_label']}))if shuffle:ds = ds.shuffle(1000)ds = ds.batch(batch_size)return ds# 轉換數據集
train_ds = df_to_dataset(train_features, train_labels)
test_ds = df_to_dataset(test_features, test_labels, shuffle=False)# 初始化模型
model = MaskWideDeep(deep_feature_columns, wide_feature_columns)
# 編譯
# loss='binary_crossentropy':使用二元交叉熵損失函數,因為 CTR 預估這是一個二分類問題
# metrics=['AUC', 'accuracy']:在訓練和評估時跟蹤兩個指標
model.compile(optimizer='adam',loss='binary_crossentropy',metrics=['AUC', 'accuracy'])# 訓練
history = model.fit(train_ds,validation_data=test_ds,epochs=10,verbose=1)# 評估
test_loss, test_auc, test_acc = model.evaluate(test_ds)
print(f"\n測試集評估結果:AUC={test_auc:.3f}, 準確率={test_acc:.3f}")# 示例:繪制訓練曲線
import matplotlib.pyplot as pltplt.plot(history.history['accuracy'], label='Train Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.plot(history.history['loss'], label='Loss')plt.legend()
plt.show()# ====================
# 5. 模型保存
# ====================
model.save('masknet_recommender')
print("模型已保存到 masknet_recommender 目錄")

4.運行結果示例

Epoch 1/10
2025-04-05 21:36:42.640541: W tensorflow/tsl/platform/profile_utils/cpu_utils.cc:128] Failed to get CPU frequency: 0 Hz
250/250 [==============================] - 2s 3ms/step - loss: 0.7177 - auc: 0.4979 - accuracy: 0.4983 - val_loss: 0.6931 - val_auc: 0.5112 - val_accuracy: 0.5035
Epoch 2/10
250/250 [==============================] - 0s 2ms/step - loss: 0.6905 - auc: 0.5549 - accuracy: 0.5414 - val_loss: 0.6929 - val_auc: 0.5099 - val_accuracy: 0.5100
Epoch 3/10
250/250 [==============================] - 0s 2ms/step - loss: 0.6825 - auc: 0.6307 - accuracy: 0.5932 - val_loss: 0.6937 - val_auc: 0.5159 - val_accuracy: 0.5115
Epoch 4/10
250/250 [==============================] - 0s 2ms/step - loss: 0.6683 - auc: 0.7068 - accuracy: 0.6495 - val_loss: 0.6948 - val_auc: 0.5138 - val_accuracy: 0.5005
Epoch 5/10
250/250 [==============================] - 0s 2ms/step - loss: 0.6474 - auc: 0.7747 - accuracy: 0.7067 - val_loss: 0.6967 - val_auc: 0.5169 - val_accuracy: 0.5105
Epoch 6/10
250/250 [==============================] - 0s 2ms/step - loss: 0.6224 - auc: 0.8206 - accuracy: 0.7418 - val_loss: 0.7007 - val_auc: 0.5154 - val_accuracy: 0.5075
Epoch 7/10
250/250 [==============================] - 0s 2ms/step - loss: 0.5953 - auc: 0.8496 - accuracy: 0.7726 - val_loss: 0.7066 - val_auc: 0.5169 - val_accuracy: 0.5075
Epoch 8/10
250/250 [==============================] - 0s 2ms/step - loss: 0.5666 - auc: 0.8707 - accuracy: 0.7901 - val_loss: 0.7144 - val_auc: 0.5151 - val_accuracy: 0.5090
Epoch 9/10
250/250 [==============================] - 0s 2ms/step - loss: 0.5376 - auc: 0.8891 - accuracy: 0.8030 - val_loss: 0.7247 - val_auc: 0.5164 - val_accuracy: 0.5085
Epoch 10/10
250/250 [==============================] - 0s 2ms/step - loss: 0.5103 - auc: 0.9007 - accuracy: 0.8167 - val_loss: 0.7351 - val_auc: 0.5189 - val_accuracy: 0.5205
63/63 [==============================] - 0s 972us/step - loss: 0.7351 - auc: 0.5189 - accuracy: 0.5205測試集評估結果:AUC=0.519, 準確率=0.521模型已保存到 masknet_recommender 目錄

4.1 訓練過程可視化

由于是隨機生成的數據,訓練 Accuracy 雖然隨著訓練的進行提高,但是驗證 Accuracy 卻未明顯升高。
在這里插入圖片描述

4.2 在其他工程調用模型

# 導入必要的庫
import tensorflow as tf
import pandas as pd
import numpy as np# 人工構造數據
num_users = 100
num_items = 200# 重新生成新的樣本,模擬真實數據進行預測
def generate_new_samples(num_samples=5):new_samples = []for _ in range(num_samples):user_id = np.random.randint(1, num_users + 1)item_id = np.random.randint(1, num_items + 1)user_age = np.random.randint(18, 65)user_gender = np.random.choice(['male', 'female'])user_occupation = np.random.choice(['student', 'worker', 'teacher'])city_code = np.random.randint(1, 2856)device_type = np.random.randint(0, 5)item_category = np.random.choice(['electronics', 'books', 'clothing'])item_brand = np.random.choice(['brandA', 'brandB', 'brandC'])item_price = np.random.randint(1, 199)new_samples.append({'user_id': user_id,'user_age': user_age,'user_gender': user_gender,'user_occupation': user_occupation,'city_code': city_code,'device_type': device_type,'item_id': item_id,'item_category': item_category,'item_brand': item_brand,'item_price': item_price})return pd.DataFrame(new_samples)# 生成新的樣本數據并預覽
new_samples = generate_new_samples(num_samples=5)
# 設置display.max_columns為None,強制顯示全部列:
pd.set_option('display.max_columns', None)
print("\nGenerated New Samples:\n", new_samples)test_sample = {'user_id': tf.convert_to_tensor(new_samples['user_id'].values, dtype=tf.int64),'user_age': tf.convert_to_tensor(new_samples['user_age'].values, dtype=tf.int64),'user_gender': tf.convert_to_tensor(new_samples['user_gender'].values, dtype=tf.string),'user_occupation': tf.convert_to_tensor(new_samples['user_occupation'].values, dtype=tf.string),'city_code': tf.convert_to_tensor(new_samples['city_code'].values, dtype=tf.int64),'device_type': tf.convert_to_tensor(new_samples['device_type'].values, dtype=tf.int64),'item_id': tf.convert_to_tensor(new_samples['item_id'].values, dtype=tf.int64),'item_category': tf.convert_to_tensor(new_samples['item_category'].values, dtype=tf.string),'item_brand': tf.convert_to_tensor(new_samples['item_brand'].values, dtype=tf.string),'item_price': tf.convert_to_tensor(new_samples['item_price'].values, dtype=tf.int64)
}# 從目錄加載模型
best_model = tf.keras.models.load_model('masknet_recommender')
# 明確使用默認簽名
predict_fn = best_model.signatures['serving_default']
# 進行預測
prediction = predict_fn(**test_sample)
# 提取并打印預測結果
predicted_ctr = prediction['output_1'].numpy().flatten()
new_samples['ctr_prob'] = predicted_ctr
print("\nPrediction Results:")
for idx, row in new_samples.iterrows():print(f"Item ID: {row['item_id']} | CTR Score: {row['ctr_prob']:.4f}")

運行結果示例:

Generated New Samples:user_id  user_age user_gender user_occupation  city_code  device_type  \
0       91        49      female         student       2223            4   
1       61        38      female          worker        103            3   
2       93        52      female          worker        921            0   
3       31        47        male         teacher       1482            3   
4       47        32      female         student       1582            1   item_id item_category item_brand  item_price  
0      151      clothing     brandA         189  
1       14   electronics     brandB           8  
2      107      clothing     brandB          81  
3       12         books     brandC         164  
4       69   electronics     brandC          39  
Metal device set to: Apple M1 ProsystemMemory: 16.00 GB
maxCacheSize: 5.33 GB2025-04-05 21:42:49.803612: W tensorflow/tsl/platform/profile_utils/cpu_utils.cc:128] Failed to get CPU frequency: 0 HzPrediction Results:
Item ID: 151 | CTR Score: 0.5109
Item ID: 14 | CTR Score: 0.5160
Item ID: 107 | CTR Score: 0.5195
Item ID: 12 | CTR Score: 0.4289
Item ID: 69 | CTR Score: 0.5206

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/75705.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75705.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/75705.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【爬蟲案例】采集 Instagram 平臺數據幾種方式(python腳本可直接運行)

更多內容請見: 爬蟲和逆向教程-專欄介紹和目錄 文章目錄 一、概述1.1 Instagram基礎信息1.2 Instagram平臺架構核心技術棧1.3 采集提示1.4 幾種采集方案對比二、四種采集方案分析三、寫爬蟲采集Instagram案例3.1 采集作品信息并下載視頻或圖片(無需登錄)3.2 explore接口的采…

OFP--2018

文章目錄 AbstractIntroductionRelated Work2D object detection3D object detection from LiDAR3D object detection from imagesIntegral images 3D Object Detection ArchitectureFeature extractionOrthographic feature transformFast average pooling with integral imag…

LINUX 4 tar -zcvf -jcvf -Jcvf -tf -uf

cp -r mv: 1.移動文件到目錄 2.文件改名 3.目錄改名 s 上面是打包 下面是打包并壓縮

linux signal up/down/down_interruptiable\down_uninterruptiable使用

在Linux內核中,down, down_interruptible, down_killable, 和 up 是用于操作信號量(semap hores)的函數,它們用于進程同步和互斥。以下是對這些函數的簡要說明。 1,down(&sem): 這個函數用于獲取信號量。如果信號…

使用人工智能大模型DeepSeek,如何進行論文潤色和去重?

今天我們學習人工智能,如何協助我們進行論文潤色和去重。手把手的學習視頻地址請訪問https://edu.csdn.net/learn/40402/666422 第一步在騰訊元寶對話框中輸入如何協助老師做論文潤色,通過提問,我們了解了老師寫論文潤色的步驟和建議。潤色的…

UE5 Simulation Stage

首先將Grid2D創建出來,然后設置值,Grid2D類似于在Niagara系統中的RenderTarget2D,可以進行繪制,那么設置大小為512 * 512 開啟Niagara粒子中的Simulation Stage 然后開始編寫我們的自定義模塊 模塊很簡單,TS就是Textur…

OpenCV 圖形API(6)將一個矩陣(或圖像)與一個標量值相加的函數addC()

操作系統:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 編程語言:C11 算法描述 addC 函數將給定的標量值加到給定矩陣的每個元素上。該功能可以用矩陣表達式替換: dst src1 c \texttt{dst} \texttt{src1} \te…

多GPU訓練

寫在前面 限于財力不足,本機上只有一個 GPU 可供使用,因此這部分的代碼只能夠稍作了解,能夠使用的 GPU 也只有一個。 多 GPU 的數據并行:有幾張卡,對一個小批量數據,有幾張卡就分成幾塊,每個 …

0基礎 | 硬件 | 電源系統 一

降壓電路LDO 幾乎所有LDO都是基于此拓撲結構 圖 拓撲結構 LDO屬于線性電源,通過控制開關管的導通程度實現穩壓,輸出紋波小,無開關噪聲 線性電源,IoutIin,發熱功率P電壓差△U*電流I,轉換效率Vo/Vi LDO不適…

mysql數據庫中getshell的方式總結

mysql數據庫中getshell的方式總結 MySQL版本大于5.0,MySQL 5.0版本以上會創建日志文件,我們通過修改日志文件的全局變量,就可以GetSHELL,下面這篇文章主要給大家介紹了關于mysql數據庫中getshell的方式,需要的朋友可以參考下 outfile和dumpfile寫shell 利用條件 …

基于Python的微博數據采集

摘要 本系統通過逆向工程微博移動端API接口,實現了對熱門板塊微博內容及用戶評論的自動化采集。系統采用Requests+多線程架構,支持遞歸分頁采集和動態請求頭模擬,每小時可處理3000+條數據記錄。關鍵技術特征包括:1)基于max_id的評論分頁遞歸算法 2)HTML標簽清洗正則表達…

WiFi加密協議

目錄 1. 認證(Authentication)? ?1.1 開放系統認證(Open System Authentication)? 1.2 共享密鑰認證(Shared Key Authentication)? ?1.3 802.1X/EAP認證(企業級認證)? ?2. 關聯(Association)? ?3. 加密協議(Security Handshake)? ?整體流程總結?…

MySQL篇(六)MySQL 分庫分表:應對數據增長挑戰的有效策略

MySQL篇(六)MySQL 分庫分表:應對數據增長挑戰的有效策略 MySQL篇(六)MySQL 分庫分表:應對數據增長挑戰的有效策略一、引言二、為什么需要分庫分表2.1 性能瓶頸2.2 存儲瓶頸2.3 高并發壓力 三、分庫分表的方…

極限編程(XP)簡介及其價值觀與最佳實踐

目錄 一、什么是極限編程(XP)二、極限編程的核心價值觀1. 溝通2. 簡單3. 反饋4. 勇氣 三、極限編程的12個最佳實踐1. 結對編程2. 40小時工作制3. 簡單設計4. 代碼規范5. 測試驅動開發(TDD)6. 系統隱喻7. 持續集成8. 重構9. 客戶在…

Java進階-day06:反射、注解與動態代理深度解析

目錄 一、反射機制:Java的自我認知能力 1.1 認識反射 1.2 獲取Class對象 1.3 獲取類的成分 二、注解:Java的元數據機制 2.1 注解概述 2.2 元注解 2.3 注解解析 2.4 注解的實際應用 三、動態代理:靈活的間接訪問機制 3.1 為什么需要…

Nacos注冊中心AP模式核心源碼分析(集群模式)

文章目錄 概述一、客戶端新注冊實例信息在集群間同步二、服務端集群節點信息在集群間同步2.1、DistroMapper2.2、ProtocolManager2.3、ServerListManager2.4、RaftPeerSet 三、客戶端實例狀態信息在集群間同步四、服務端新節點上線同步集群數據 概述 在Nacos集群模式下&#xf…

vscode和cursor對ubuntu22.04的remote ssh和X-Windows的無密碼登錄

這里寫自定義目錄標題 寫在前面需求的描述問題的引出 昨天已使能自動登錄上午我的改變UBUNTU 22.04關閉密碼規則一:修改 /etc/pam.d/common-password 文件二:修改 /etc/security/pwquality.conf 文件方法三:禁用 pam_pwquality.so 模塊 vscod…

論文閱讀:基于增強通用深度圖像水印的混合篡改定位技術 OmniGuard

一、論文信息 論文名稱:OmniGuard: Hybrid Manipulation Localization via Augmented Versatile Deep Image Watermarking作者團隊:北京大學發表會議:CVPR2025論文鏈接:https://arxiv.org/pdf/2412.01615二、動機與貢獻 動機: 隨著生成式 AI 的快速發展,其在圖像編輯領…

一周學會Pandas2 Python數據處理與分析-NumPy數組創建

鋒哥原創的Pandas2 Python數據處理與分析 視頻教程: 2025版 Pandas2 Python數據處理與分析 視頻教程(無廢話版) 玩命更新中~_嗶哩嗶哩_bilibili NumPy數組創建最常用的方式是直接創建, numpy 可以直接創建或者將 python的其他元素轉為 array 對象。 下…

【全球首發】DeepSeek谷歌版1.1.5 - 免費GPT-4級別AI工具

【全球首發】DeepSeek谷歌版1.1.5 - 免費GPT-4級別AI工具 資源簡介 DeepSeek谷歌版1.1.5是目前全球領先的免費AI助手,性能超越國內主流AI產品,提供類似GPT-4的智能體驗。 版本信息 最新版本:1.1.5(2024最新版)應用…