推荐系统(二十二):基于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
相关文章:
推荐系统(二十二):基于MaskNet和WideDeep的商品推荐CTR模型实现
在上一篇文章《推荐系统(二十一):基于MaskNet的商品推荐CTR模型实现》中,笔者基于 MaskNet 构建了一个简单的模型。笔者所经历的工业级实践证明,将 MaskNet 和 Wide&Deep 结合应用,可以取得不错的效果&…...
辅助查询是根据查询到的文档片段再去生成新的查询问题
💡 辅助查询是怎么来的? 它是基于你当前查询(query)检索到的某个文档片段(chunk_result),再去“反推”出新的相关问题(utility queries),这些问题的作用是&a…...
Spring Cloud 框架为什么能处理高并发
Spring Cloud框架能够有效处理高并发场景,核心在于其微服务架构设计及多组件的协同作用,具体机制如下: 一、分布式架构设计支撑高扩展性 服务拆分与集群部署 Spring Cloud通过微服务拆分将单体系统解耦为独立子服务,每个服务可独…...
Pseduo LiDAR(CVPR2019)
文章目录 AbstractIntroductionRelated WorkLiDAR-based 3D object detectionStereo- and monocular-based depth estimationImage-based 3D object detection MethodDepth estimationPseudo-LiDAR generationLiDAR vs. pseudo-LiDAR3D object detectionData representation ma…...
强化学习课程:stanford_cs234 学习笔记(3)introduction to RL
文章目录 前言7 markov 实践7.1 markov 过程再叙7.2 markov 奖励过程 MRP(markov reward process)7.3 markov 价值函数与贝尔曼方程7.4 markov 决策过程MDP(markov decision process)的 状态价值函数7.4.1 状态价值函数7.4.2 状态…...
前端精度计算:Decimal.js 基本用法与详解
一、Decimal.js 简介 decimal.js 是一个用于任意精度算术运算的 JavaScript 库,它可以完美解决浮点数计算中的精度丢失问题。 官方API文档:Decimal.js 特性: 任意精度计算:支持大数、小数的高精度运算。 链式调用:…...
来聊聊C++中的vector
一.vector简介 vector是什么 C 中的 vector 是一种序列容器,它允许你在运行时动态地插入和删除元素。 vector 是基于数组的数据结构,但它可以自动管理内存,这意味着你不需要手动分配和释放内存。 与 C 数组相比,vector 具有更多的…...
对比学习中的NCE(Noise-Contrastive Estimation)和InfoNCE(SimCLR)损失函数+案例(附SimSiam分析)
在对比学习(Contrastive Learning)中,NCE(Noise-Contrastive Estimation)和InfoNCE是两种常见的目标函数,它们都用于通过区分正样本和负样本来学习高质量的表示。 1. NCE(Noise-Contrastive Est…...
基于FAN网络的图像识别系统设计与实现
基于FAN网络的图像识别系统设计与实现 一、系统概述 本系统旨在利用FAN(Fourier Analysis Networks)网络架构实现高效的图像识别功能,并通过Python语言设计一个直观的用户界面,方便用户操作与使用。FAN网络在处理周期性特征方面具有独特优势,有望提升图像识别在复杂场景…...
【瑞萨 RA-Eco-RA2E1-48PIN-V1.0 开发板测评】PWM
【瑞萨 RA-Eco-RA2E1-48PIN-V1.0 开发板测评】PWM 本文介绍了瑞萨 RA2E1 开发板使用内置时钟和定时器实现 PWM 输出以及呼吸灯的项目设计。 项目介绍 介绍了 PWM 和 RA2E1 的 PWM 资源。 PWM 脉冲宽度调制(Pulse Width Modulation, PWM)是一种对模拟…...
NDK开发:开发环境
NDK开发环境 一、NDK简介 1.1 什么是NDK NDK(Native Development Kit)是Android提供的一套工具集,允许开发者在Android应用中使用C/C++代码。它包含了: 交叉编译器构建工具调试器系统头文件和库示例代码和文档1.2 NDK的优势 性能优化:直接使用底层代码,提高性能代码保…...
设计模式简述(三)工厂模式
工厂模式 描述简单工厂(静态工厂)工厂方法模式 抽象工厂增加工厂管理类使用 描述 工厂模式用以封装复杂的实例初始化过程,供外部统一调用 简单工厂(静态工厂) 如果对象创建逻辑简单且一致,可以使用简单工…...
通过Postman和OAuth 2.0连接Dynamics 365 Online的详细步骤
🌟 引言 在企业应用开发中,Dynamics 365 Online作为微软的核心CRM平台,提供了强大的Web API接口。本文将教你如何通过Postman和OAuth 2.0认证实现与Dynamics 365的安全连接,轻松调用数据接口。 📝 准备工作 工具安装…...
LlamaIndex实现RAG增强:上下文增强检索/重排序
面向文档检索的上下文增强技术 文章目录 面向文档检索的上下文增强技术概述技术背景核心组件方法详解文档预处理向量存储创建上下文增强检索检索对比技术优势结论导入库和环境变量读取文档创建向量存储和检索器数据摄取管道使用句子分割器的摄取管道使用句子窗口的摄取管道查询…...
AI比人脑更强,因为被植入思维模型【43】蝴蝶效应思维模型
giszz的理解:蝴蝶效应我们都熟知,就是说一个微小的变化,能带动整个系统甚至系统的空间和时间的远端,产生巨大的链式反应。我学习后的启迪,简单的说,就是不要忽视任何微小的问题,更多时候&#x…...
程序化广告行业(62/89):DSP系统的媒体与PDB投放设置探秘
程序化广告行业(62/89):DSP系统的媒体与PDB投放设置探秘 大家好!在之前的学习中,我们对程序化广告的DSP系统有了一定了解。今天还是带着和大家共同进步的想法,深入探索DSP系统中媒体设置以及PDB投放设置的…...
Java项目之基于ssm的怀旧唱片售卖系统(源码+文档)
项目简介 怀旧唱片售卖系统实现了以下功能: 用户信息管理: 用户信息新增:添加新用户的信息。 用户信息修改:对现有用户信息进行修改。 商品信息管理: 商品信息添加:增加新的商品(唱片&#x…...
程序化广告行业(61/89):DSP系统活动设置深度剖析
程序化广告行业(61/89):DSP系统活动设置深度剖析 大家好!在程序化广告的学习道路上,我们已经探索了不少重要内容。今天依旧本着和大家一起学习进步的想法,深入解析DSP系统中活动设置的相关知识。这部分内容…...
Altshuller矛盾矩阵查询:基于python和streamlit
基于python和streamlit实现的Altshuller矛盾矩阵查询 import streamlit as st import json# 加载数据 st.cache_resource def load_data():with open(parameter.json, encodingutf-8) as f:parameters json.load(f)with open(way.json, encodingutf-8) as f:contradictions …...
FreeRTOS的空闲任务
在 FreeRTOS 中,空闲任务(Idle Task) 是操作系统自动创建的一个特殊任务,其作用和管理方式如下: 1. 空闲任务创建 FreeRTOS 内核自动创建:当调用 vTaskStartScheduler() 启动调度器时,内核会自…...
【代码模板】如何用FILE操作符打开文件?fopen、fclose
#include "stdio.h" #include "unistd.h"int main(int argc, char *argv[]) {FILE *fp fopen("1.log", "wb");if (!fp) {perror("Failed open 1.log");return -1;}fclose(fp); }关于权限部分参考兄弟篇【代码模板】C语言中…...
[特殊字符] Pandas 常用操作对比:Python 运算符 vs Pandas 函数
在 Pandas 中,许多操作可以直接使用 Python 的比较运算符(如 、!、>、< 等),而不需要调用 Pandas 的专门函数(如 eq()、ne()、gt() 等)。这些运算符在 Pandas 中已经被重载,代码更简洁。以…...
I.MX6ULL开发板与linux互传文件的方法--NFS,SCP,mount
1、内存卡或者U盘 方法比较简单,首先在linux系统中找到u盘对应的文件夹,随后使用cp指令将文件拷贝进u盘。 随后将u盘插入开发板中,找到u盘对应的设备文件。一般u盘对应的设备文件在/dev下,以sda开头,可以使用命令列出所…...
图解AUTOSAR_SWS_FlashEEPROMEmulation
AUTOSAR Flash EEPROM Emulation (FEE) 详解 基于AUTOSAR规范的Flash EEPROM Emulation模块分析 目录 1. 概述2. 架构设计 2.1 模块位置与接口2.2 内部状态管理2.3 配置结构3. API接口 3.1 接口功能分类3.2 错误管理4. 操作流程 4.1 写入操作序列5. 总结1. 概述 Flash EEPROM …...
Unity:Simple Follow Camera(简单相机跟随)
为什么需要Simple Follow Camera? 在游戏开发中,相机(Camera)是玩家的“眼睛”。它的作用是决定玩家看到游戏世界的哪一部分。很多游戏需要相机自动跟随玩家角色,让玩家始终可以看到角色及其周围的环境,而…...
[项目总结] 在线OJ刷题系统项目总结与分析(二): 技术应用(上)
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...
针对Ansible执行脚本时报错“可执行文件格式错误”,以下是详细的解决步骤和示例
针对Ansible执行脚本时报错“可执行文件格式错误”,以下是详细的解决步骤和示例: 目录 一、错误原因分析二、解决方案1. 检查并添加可执行权限2. 修复Shebang行3. 转换文件格式(Windows → Unix)4. 检查脚本内容兼容性5. 显式指定…...
从 Dense LLM 到 MoE LLM:以 DeepSeek MoE 为例讲解 MoE 的基本原理
写在前面 大多数 LLM 均采用 Dense(密集) 架构。这意味着,在处理每一个输入 Token 时,模型所有的参数都会被激活和计算。想象一下,为了回答一个简单的问题,你需要阅读整部大英百科全书的每一个字——这显然效率低下。 为了突破 Dense 模型的瓶颈,一种名为 Mixture of …...
未来已来:探索AI驱动的HMI设计新方向
在科技浪潮的持续冲击下,人工智能(AI)正以势不可挡的姿态重塑各个领域的格局,其中人机交互(HMI,Human - Machine Interaction)设计领域深受其影响,正经历着深刻的变革。AI 技术的融入…...
5天速成ai agent智能体camel-ai之第1天:camel-ai安装和智能体交流消息讲解(附源码,零基础可学习运行)
嗨,朋友们!👋 是不是感觉AI浪潮铺天盖地,身边的人都在谈论AI Agent、大模型,而你看着那些密密麻麻的代码,感觉像在读天书?🤯 别焦虑!你不是一个人。很多人都想抓住AI的风…...
Unity UGUI使用手册
概述 UGUI(Unity Graphical User Interface) :Unity 图像用户界面 在游戏开发中,我们经常需要搭建一些图形用户界面。Unity内置的UGUI可以帮助开发者可视化地拼接界面,提高开发效率。UGUI提供不同样式的UI组件,并且封装了对应功能的API&am…...
(二)输入输出处理——打造智能对话的灵魂
上一篇:(一)从零开始:用 LangChain 和 ZhipuAI 搭建简单对话 在上一篇文章中,我们成功搭建了一个基于 LangChain 和 ZhipuAI 的智能对话系统的基础环境。今天,我们将深入探讨输入输出处理的细节࿰…...
beego文件上传
1file.go 2html代码 3路由设置 beego.Router("/file/Upload", &controllers.FileUploadController{}, "post:Upload") 注意 1,得新建个upload文件夹 2,路由设置严格区分大小写。 biiego文件下载上传代码 github 觉得不错Star下...
代码随想录回溯算法01(递归)
回溯法也可以叫做回溯搜索法,它是一种搜索的方式。 回溯是递归的副产品,只要有递归就会有回溯。 所以以下讲解中,回溯函数也就是递归函数,指的都是一个函数。 组合问题:N个数里面按一定规则找出k个数的集合切割问题&am…...
分治-归并排序-逆序对问题
目录 1.升序(以右边的合并组为基准) 2.降序(以左边的合并组为基准) 3.逆对序--固定下标 1.升序(以右边的合并组为基准) 找出左边有多少个数比我(nums[right])大 应该在每一次合并之前,进行…...
mysql-getshell的几种方法
mysql_getshell的几种方法 mysql_getshell 一、mysql的–os-shell 利用原理 –os-shell就是使用udf提权获取WebShell。也是通过into oufile向服务器写入两个文件,一个可以直接执行系统命令,一个进行上传文件。此为sqlmap的一个命令,利用这…...
初阶数据结构--树
1. 树的概念与结构 树是⼀种⾮线性的数据结构,它是由 n(n>0) 个有限结点组成⼀个具有层次关系的集合。把它叫做 树是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,⽽叶朝下的。 有⼀个特殊的结点,称…...
搭建redis主从同步实现读写分离(原理剖析)
搭建redis主从同步实现读写分离(原理剖析) 文章目录 搭建redis主从同步实现读写分离(原理剖析)前言一、搭建主从同步二、同步原理 前言 为什么要学习redis主从同步,实现读写分析。因为单机的redis虽然是基于内存,单机并发已经能支撑很高。但是随着业务量…...
Python3 学习笔记
Python3 简介 | 菜鸟教程 一 Python3 简介 Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色…...
kmpmanacher
KMP 理论 KMP算法的核心是构建一个部分匹配表,也称为前缀表。这个表记录了模式串中每个位置之前的最长公共前缀和后缀的长度。例如,对于模式串"ababaca",其部分匹配表如下: 位置0123456字符ababaca最长公共前后缀长度…...
ts基础知识总结
TypeScript(简称TS)是JavaScript(简称JS)的一个超集,它在JS的基础上增加了静态类型检查、类、模块等特性。 TypeScript 与 JavaScript 的不同及好处 不同点 类型系统 JavaScript 是一种弱类型语言,这意味…...
操作系统内存管理
为什么要有虚拟内存 单片机的CPU直接操作内存的物理地址,这就导致在内存中同时运行两个程序是不可能的,有可能会出现第一个程序在2000的位置写入新的值将会擦掉第二个程序存放在相同位置上的内容。 出现这个问题的根本原因是两个程序引用了绝对物理地址。…...
M芯片,能运行普通应用程序的原架构虚拟机
在我们使用搭载了Apple芯片的Mac时,很多时候会用到windows虚拟机来使用windows应用程序 但是Apple芯片是ARM架构,如果运行原价构的虚拟机,很多64位的普通应用程序就无法运行,如果使用UTM来安装64位的跨架构虚拟机,就会非常卡慢 但实际上使用一种特殊的系统镜像,就可以使用ARM…...
多功能指示牌的主要功能有哪些?
哇哦!咱们的多功能指示牌可有着超多超厉害的主要功能哦,简直就是生活中的超级小助手,涵盖了方方面面呢! 指示导向功能 道路指引:不管是在繁华热闹的城市道路,还是车水马龙的高速公路,亦或是风…...
Superset 问题
和nginx结合使用,如果不是配置到根路径,会比较麻烦,我试了很多种方法,也就 这个 靠谱点,不过,我最后还是选择的部署在根路径,先探索一番再说默认不能选择mysql数据库,需要安装mysql客…...
安装gpu版本的dgl
1.先去网址,找到对应版本的dgl,然后下载到本地。 dgl-whl下载地址 我的是python 3.8 ,cuda 11.6. windows 2.在虚拟环境里 输入 pip install E:\dgl-1.0.2cu116-cp38-cp38-win_amd64.whl (因为我下载到E盘里了) 这样GPU版本的d…...
vue watch和 watchEffect
在 Vue 3 中,watch 和 watchEffect 是两个用于响应式地监听数据变化并执行副作用的 API。它们在功能上有一些相似之处,但用途和行为有所不同。以下是对 watch 和 watchEffect 的详细对比和解释: 1. watch watch 是一个更通用的 API…...
JavaScript基础--03-变量的数据类型:基本数据类型和引用数据类型
JavaScript基础--03-变量的数据类型:基本数据类型和引用数据类型 前言变量的数据类型为什么需要数据类型JS中一共有六种数据类型 一个经典的例子栈内存和堆内存 前言 我们接着上一篇文章 JavaScript基础–02-变量 来讲。 下一篇文章 JavaScript基础–04-基本数据类…...
WindowsPE文件格式入门05.PE加载器LoadPE
https://bpsend.net/thread-316-1-1.html LoadPE - pe 加载器 壳的前身 如果想访问一个程序运行起来的内存,一种方法就是跨进程读写内存,但是跨进程读写内存需要来回调用api,不如直接访问地址来得方便,那么如果我们需要直接访问地址,该怎么做呢?.需要把dll注进程,注进去的代码…...
【Redis】通用命令
使用者通过redis-cli客户端和redis服务器交互,涉及到很多的redis命令,redis的命令非常多,我们需要多练习常用的命令,以及学会使用redis的文档。 一、get和set命令(最核心的命令) Redis中最核心的两个命令&…...