我们首先模拟用户数据:假设有1000个用户,每个用户有100个特征。这些特征可能包括数值型、类别型等,但为了简化,我们先生成混合类型的模拟数据。
然后,我们将使用几种方法来生成embedding,包括:
- 直接标准化后拼接(作为基线)
- 使用自编码器进行降维
- 使用监督学习(假设我们有任务标签)训练一个神经网络,并提取中间层作为embedding
我们将按照以下步骤进行:
步骤1:生成模拟数据
步骤2:数据预处理(标准化、编码等)
步骤3:构建模型
步骤4:训练模型(如果是自编码器或监督学习)
步骤5:提取embedding
由于没有具体任务,我们可以用自编码器来学习压缩表示,或者用监督学习(假设一个二分类任务)来学习有意义的表示。
这里,我们将模拟一个二分类任务(例如用户是否点击广告),然后训练一个监督模型,并提取中间层的输出作为embedding。
另外,我们也可以使用无监督的自编码器。
我们将分别展示两种方法。
注意:由于是模拟数据,我们无法保证学到的embedding的实际意义,但代码结构是完整的。
开始写代码:
我来为你编写一个完整的、可运行的Python代码,模拟用户数据并生成embedding嵌入表示。
python
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import seaborn as sns
from torch.utils.data import DataLoader, TensorDataset
# 设置随机种子确保可重复性
np.random.seed(42)
torch.manual_seed(42)
# 1. 模拟用户数据
def generate_synthetic_users(n_users=1000, n_features=100):
"""生成模拟用户数据"""
print(f"正在生成 {n_users} 个用户的模拟数据,每个用户有 {n_features} 个特征...")
# 用户ID
user_ids = [f"user_{i:04d}" for i in range(n_users)]
# 创建空的数据字典
data = {"user_id": user_ids}
# 模拟不同类型的数据特征
# 1. 基本人口统计特征 (20个特征)
data["age"] = np.random.normal(35, 10, n_users).clip(18, 70) # 年龄
data["income"] = np.random.lognormal(10, 0.5, n_users) # 收入,对数正态分布
data["education_years"] = np.random.choice([9, 12, 16, 18, 21], n_users, p=[0.1, 0.3, 0.4, 0.15, 0.05]) # 教育年限
# 2. 行为特征 (40个特征) - 模拟用户在不同类别上的活跃度
categories = ["娱乐", "科技", "体育", "购物", "旅游", "美食", "健康", "金融", "教育", "游戏"]
for i in range(10):
for j in range(4): # 每个类别有4个行为指标
feature_name = f"{categories[i]}_行为_{j}"
# 使用不同的分布模拟不同行为
if j == 0:
data[feature_name] = np.random.exponential(scale=2, size=n_users) # 浏览时长
elif j == 1:
data[feature_name] = np.random.poisson(lam=3, size=n_users) # 访问次数
elif j == 2:
data[feature_name] = np.random.beta(2, 5, size=n_users) * 100 # 互动率
else:
data[feature_name] = np.random.randint(0, 100, size=n_users) # 评分
# 3. 偏好特征 (20个特征) - 0-1之间的偏好得分
for i in range(20):
data[f"偏好_{i}"] = np.random.beta(2, 2, size=n_users)
# 4. 设备和使用特征 (10个特征)
data["设备类型"] = np.random.choice(["移动端", "PC", "平板"], n_users, p=[0.6, 0.3, 0.1])
data["使用频率"] = np.random.choice(["每天", "每周", "每月", "偶尔"], n_users, p=[0.4, 0.3, 0.2, 0.1])
for i in range(8):
data[f"功能使用_{i}"] = np.random.randint(0, 10, size=n_users)
# 5. 社交特征 (10个特征)
data["好友数"] = np.random.pareto(2.5, size=n_users).clip(0, 500) + 10
data["关注数"] = np.random.randint(0, 200, size=n_users)
data["粉丝数"] = np.random.poisson(lam=50, size=n_users)
for i in range(7):
data[f"社交互动_{i}"] = np.random.exponential(scale=5, size=n_users)
# 转换为DataFrame
df = pd.DataFrame(data)
# 确保正好有100个特征
# 如果不够,添加一些随机特征
current_features = len(df.columns) - 1 # 减去user_id列
if current_features < n_features:
for i in range(n_features - current_features):
df[f"附加特征_{i}"] = np.random.normal(0, 1, n_users)
print(f"数据生成完成!实际特征数: {len(df.columns)-1} (包含user_id)")
return df
# 2. 数据预处理
def preprocess_data(df, target_column=None):
"""预处理数据:处理缺失值、编码分类变量、标准化数值特征"""
print("\n正在预处理数据...")
# 复制数据,避免修改原始数据
df_processed = df.copy()
# 分离用户ID
user_ids = df_processed['user_id']
df_processed = df_processed.drop('user_id', axis=1)
# 处理缺失值
if df_processed.isnull().any().any():
print(f"发现 {df_processed.isnull().sum().sum()} 个缺失值,使用中位数填充...")
df_processed = df_processed.fillna(df_processed.median())
# 识别数值特征和分类特征
numerical_features = df_processed.select_dtypes(include=[np.number]).columns.tolist()
categorical_features = df_processed.select_dtypes(include=['object']).columns.tolist()
print(f"数值特征: {len(numerical_features)} 个")
print(f"分类特征: {len(categorical_features)} 个")
# 编码分类特征
label_encoders = {}
for col in categorical_features:
le = LabelEncoder()
df_processed[col] = le.fit_transform(df_processed[col])
label_encoders[col] = le
# 标准化数值特征
scaler = StandardScaler()
df_processed[numerical_features] = scaler.fit_transform(df_processed[numerical_features])
# 如果有目标变量,分离出来
target = None
if target_column and target_column in df.columns:
target = df[target_column]
df_processed = df_processed.drop(target_column, axis=1) if target_column in df_processed.columns else df_processed
print(f"预处理完成!最终特征维度: {df_processed.shape[1]}")
return df_processed, user_ids, scaler, label_encoders, target
# 3. 使用PCA生成embedding(简单方法)
def generate_embeddings_pca(features, n_components=32):
"""使用PCA降维生成embedding"""
print(f"\n使用PCA生成{features.shape[0]}个用户的embedding,维度: {n_components}...")
pca = PCA(n_components=n_components, random_state=42)
embeddings = pca.fit_transform(features)
print(f"PCA解释方差比: {pca.explained_variance_ratio_.sum():.3f}")
print(f"前5个主成分解释方差比: {pca.explained_variance_ratio_[:5]}")
return embeddings, pca
# 4. 使用自编码器生成embedding(深度学习方法)
class Autoencoder(nn.Module):
"""自编码器模型,用于学习低维表示"""
def __init__(self, input_dim, embedding_dim):
super(Autoencoder, self).__init__()
# 编码器
self.encoder = nn.Sequential(
nn.Linear(input_dim, 256),
nn.BatchNorm1d(256),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(256, 128),
nn.ReLU(),
nn.Linear(128, embedding_dim)
)
# 解码器
self.decoder = nn.Sequential(
nn.Linear(embedding_dim, 128),
nn.ReLU(),
nn.Linear(128, 256),
nn.ReLU(),
nn.Linear(256, input_dim)
)
def forward(self, x):
embedding = self.encoder(x)
reconstructed = self.decoder(embedding)
return embedding, reconstructed
def train_autoencoder(features, embedding_dim=32, epochs=50, batch_size=64, learning_rate=0.001):
"""训练自编码器并生成embedding"""
print(f"\n训练自编码器生成embedding,维度: {embedding_dim}...")
# 转换为PyTorch张量
features_tensor = torch.FloatTensor(features)
# 创建数据加载器
dataset = TensorDataset(features_tensor, features_tensor)
train_loader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
# 初始化模型
input_dim = features.shape[1]
model = Autoencoder(input_dim, embedding_dim)
# 损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
# 训练循环
model.train()
train_losses = []
for epoch in range(epochs):
epoch_loss = 0.0
for batch_features, _ in train_loader:
optimizer.zero_grad()
_, reconstructed = model(batch_features)
loss = criterion(reconstructed, batch_features)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
avg_loss = epoch_loss / len(train_loader)
train_losses.append(avg_loss)
if (epoch + 1) % 10 == 0:
print(f"Epoch [{epoch+1}/{epochs}], Loss: {avg_loss:.6f}")
# 生成所有数据的embedding
model.eval()
with torch.no_grad():
all_embeddings = []
for batch_features, _ in train_loader:
embeddings, _ = model(batch_features)
all_embeddings.append(embeddings)
embeddings = torch.cat(all_embeddings, dim=0).numpy()
print(f"自编码器训练完成!最终损失: {train_losses[-1]:.6f}")
return embeddings, model, train_losses
# 5. 使用监督学习生成embedding(假设有标签)
class SupervisedEmbeddingModel(nn.Module):
"""监督学习模型,用于学习有任务意义的embedding"""
def __init__(self, input_dim, embedding_dim, n_classes=2):
super(SupervisedEmbeddingModel, self).__init__()
# 共享编码层
self.encoder = nn.Sequential(
nn.Linear(input_dim, 128),
nn.BatchNorm1d(128),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, embedding_dim)
)
# 分类头
self.classifier = nn.Sequential(
nn.Linear(embedding_dim, 32),
nn.ReLU(),
nn.Linear(32, n_classes)
)
def forward(self, x):
embedding = self.encoder(x)
logits = self.classifier(embedding)
return embedding, logits
def train_supervised_model(features, labels, embedding_dim=32, epochs=50, batch_size=64, learning_rate=0.001):
"""训练监督模型并提取embedding"""
print(f"\n训练监督模型生成embedding,维度: {embedding_dim}...")
# 转换为PyTorch张量
features_tensor = torch.FloatTensor(features)
labels_tensor = torch.LongTensor(labels)
# 划分训练集和验证集
X_train, X_val, y_train, y_val = train_test_split(
features_tensor.numpy(), labels_tensor.numpy(), test_size=0.2, random_state=42
)
X_train = torch.FloatTensor(X_train)
X_val = torch.FloatTensor(X_val)
y_train = torch.LongTensor(y_train)
y_val = torch.LongTensor(y_val)
# 创建数据加载器
train_dataset = TensorDataset(X_train, y_train)
val_dataset = TensorDataset(X_val, y_val)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
# 初始化模型
input_dim = features.shape[1]
n_classes = len(np.unique(labels))
model = SupervisedEmbeddingModel(input_dim, embedding_dim, n_classes)
# 损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
# 训练循环
model.train()
train_losses = []
val_accuracies = []
for epoch in range(epochs):
# 训练阶段
model.train()
epoch_loss = 0.0
for batch_features, batch_labels in train_loader:
optimizer.zero_grad()
_, logits = model(batch_features)
loss = criterion(logits, batch_labels)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
avg_train_loss = epoch_loss / len(train_loader)
train_losses.append(avg_train_loss)
# 验证阶段
model.eval()
correct = 0
total = 0
with torch.no_grad():
for batch_features, batch_labels in val_loader:
_, logits = model(batch_features)
_, predicted = torch.max(logits, 1)
total += batch_labels.size(0)
correct += (predicted == batch_labels).sum().item()
val_accuracy = correct / total
val_accuracies.append(val_accuracy)
if (epoch + 1) % 10 == 0:
print(f"Epoch [{epoch+1}/{epochs}], Loss: {avg_train_loss:.4f}, Val Accuracy: {val_accuracy:.4f}")
# 生成所有数据的embedding
model.eval()
all_embeddings = []
with torch.no_grad():
for i in range(0, len(features_tensor), batch_size):
batch = features_tensor[i:i+batch_size]
embeddings, _ = model(batch)
all_embeddings.append(embeddings)
embeddings = torch.cat(all_embeddings, dim=0).numpy()
print(f"监督模型训练完成!最终验证准确率: {val_accuracies[-1]:.4f}")
return embeddings, model, train_losses, val_accuracies
# 6. 可视化embedding
def visualize_embeddings(embeddings, labels=None, method="PCA", n_components=2):
"""可视化embedding"""
print(f"\n使用{n_components}维可视化embedding...")
# 如果embedding维度高于2,使用PCA或t-SNE降维
if embeddings.shape[1] > 2:
if method == "PCA":
reducer = PCA(n_components=n_components, random_state=42)
else:
from sklearn.manifold import TSNE
reducer = TSNE(n_components=n_components, random_state=42, perplexity=30)
embeddings_2d = reducer.fit_transform(embeddings)
else:
embeddings_2d = embeddings
# 绘制散点图
plt.figure(figsize=(10, 8))
if labels is not None:
scatter = plt.scatter(embeddings_2d[:, 0], embeddings_2d[:, 1],
c=labels, cmap='viridis', alpha=0.6, s=30)
plt.colorbar(scatter, label='类别')
else:
plt.scatter(embeddings_2d[:, 0], embeddings_2d[:, 1], alpha=0.6, s=30)
plt.title(f'用户Embedding可视化 ({method})')
plt.xlabel('维度1')
plt.ylabel('维度2')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 7. 评估embedding质量
def evaluate_embeddings(embeddings, labels=None):
"""评估embedding的质量"""
print("\n评估embedding质量...")
# 1. 计算embedding的基本统计
print(f"Embedding形状: {embeddings.shape}")
print(f"Embedding均值: {embeddings.mean():.4f}")
print(f"Embedding标准差: {embeddings.std():.4f}")
# 2. 计算类内距离和类间距离(如果有标签)
if labels is not None:
unique_labels = np.unique(labels)
within_distances = []
between_distances = []
for label in unique_labels:
# 类内距离
class_samples = embeddings[labels == label]
if len(class_samples) > 1:
centroid = class_samples.mean(axis=0)
distances = np.linalg.norm(class_samples - centroid, axis=1)
within_distances.extend(distances)
# 类间距离
other_labels = unique_labels[unique_labels != label]
for other_label in other_labels:
other_centroid = embeddings[labels == other_label].mean(axis=0)
between_distance = np.linalg.norm(centroid - other_centroid)
between_distances.append(between_distance)
if within_distances and between_distances:
print(f"平均类内距离: {np.mean(within_distances):.4f}")
print(f"平均类间距离: {np.mean(between_distances):.4f}")
print(f"分离度(类间/类内): {np.mean(between_distances)/np.mean(within_distances):.4f}")
# 3. 计算最近邻的一致性(如果有原始特征)
# 这里可以添加更多评估指标
return embeddings
# 8. 主函数
def main():
"""主函数:生成用户数据并创建embedding"""
print("="*60)
print("用户Embedding生成系统")
print("="*60)
# 生成模拟用户数据
df_users = generate_synthetic_users(n_users=1000, n_features=100)
# 创建模拟标签(假设是二分类任务:高价值用户 vs 普通用户)
# 基于收入和行为特征创建标签
df_users['high_value'] = ((df_users['income'] > df_users['income'].quantile(0.7)) &
(df_users['娱乐_行为_0'] > df_users['娱乐_行为_0'].quantile(0.6))).astype(int)
# 显示数据示例
print("\n前5个用户的数据示例:")
print(df_users.head())
# 数据预处理
features, user_ids, scaler, label_encoders, target = preprocess_data(
df_users, target_column='high_value'
)
print(f"\n处理后的特征矩阵形状: {features.shape}")
print(f"目标变量分布:\n{pd.Series(target).value_counts()}")
# 方法1: 使用PCA生成embedding
embeddings_pca, pca_model = generate_embeddings_pca(features, n_components=32)
# 方法2: 使用自编码器生成embedding
embeddings_ae, ae_model, ae_losses = train_autoencoder(
features.values, embedding_dim=32, epochs=50, batch_size=64
)
# 方法3: 使用监督学习生成embedding(需要标签)
embeddings_supervised, supervised_model, train_losses, val_accuracies = train_supervised_model(
features.values, target.values, embedding_dim=32, epochs=50, batch_size=64
)
# 可视化训练过程
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
# 自编码器损失
axes[0].plot(ae_losses)
axes[0].set_title('自编码器训练损失')
axes[0].set_xlabel('Epoch')
axes[0].set_ylabel('MSE Loss')
axes[0].grid(True, alpha=0.3)
# 监督模型损失和准确率
ax2 = axes[1].twinx()
line1, = axes[1].plot(train_losses, 'b-', label='训练损失')
line2, = ax2.plot(val_accuracies, 'r-', label='验证准确率')
axes[1].set_xlabel('Epoch')
axes[1].set_ylabel('损失', color='b')
ax2.set_ylabel('准确率', color='r')
axes[1].set_title('监督模型训练过程')
axes[1].legend(handles=[line1, line2], loc='upper right')
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 可视化不同方法生成的embedding
visualize_embeddings(embeddings_pca, labels=target.values, method="PCA", n_components=2)
visualize_embeddings(embeddings_ae, labels=target.values, method="PCA", n_components=2)
visualize_embeddings(embeddings_supervised, labels=target.values, method="PCA", n_components=2)
# 评估不同方法生成的embedding
print("\n" + "="*60)
print("PCA Embedding评估:")
print("="*60)
evaluate_embeddings(embeddings_pca, labels=target.values)
print("\n" + "="*60)
print("自编码器Embedding评估:")
print("="*60)
evaluate_embeddings(embeddings_ae, labels=target.values)
print("\n" + "="*60)
print("监督学习Embedding评估:")
print("="*60)
evaluate_embeddings(embeddings_supervised, labels=target.values)
# 创建包含所有embedding的DataFrame
embedding_dfs = {}
# PCA embedding
pca_cols = [f"pca_dim_{i}" for i in range(embeddings_pca.shape[1])]
df_pca = pd.DataFrame(embeddings_pca, columns=pca_cols)
df_pca['user_id'] = user_ids.values
df_pca['embedding_method'] = 'pca'
embedding_dfs['pca'] = df_pca
# 自编码器embedding
ae_cols = [f"ae_dim_{i}" for i in range(embeddings_ae.shape[1])]
df_ae = pd.DataFrame(embeddings_ae, columns=ae_cols)
df_ae['user_id'] = user_ids.values
df_ae['embedding_method'] = 'autoencoder'
embedding_dfs['autoencoder'] = df_ae
# 监督学习embedding
supervised_cols = [f"supervised_dim_{i}" for i in range(embeddings_supervised.shape[1])]
df_supervised = pd.DataFrame(embeddings_supervised, columns=supervised_cols)
df_supervised['user_id'] = user_ids.values
df_supervised['embedding_method'] = 'supervised'
embedding_dfs['supervised'] = df_supervised
# 合并所有embedding
all_embeddings = pd.concat(embedding_dfs.values(), ignore_index=True)
# 保存embedding到文件
output_file = "user_embeddings.csv"
all_embeddings.to_csv(output_file, index=False)
print(f"\n所有embedding已保存到: {output_file}")
# 显示embedding示例
print("\nPCA生成的embedding示例 (前3个用户,前10个维度):")
print(df_pca[['user_id'] + pca_cols[:10]].head(3))
return {
'df_users': df_users,
'features': features,
'embeddings': {
'pca': embeddings_pca,
'autoencoder': embeddings_ae,
'supervised': embeddings_supervised
},
'models': {
'pca': pca_model,
'autoencoder': ae_model,
'supervised': supervised_model
},
'embedding_dfs': embedding_dfs,
'all_embeddings': all_embeddings
}
# 9. 使用示例:如何查找相似用户
def find_similar_users(user_id, embeddings_df, method='pca', n_similar=5):
"""查找与给定用户最相似的用户"""
print(f"\n查找与 {user_id} 最相似的 {n_similar} 个用户 (方法: {method})...")
# 筛选指定方法的embedding
method_df = embeddings_df[embeddings_df['embedding_method'] == method].copy()
# 获取目标用户的embedding
user_embedding = method_df[method_df['user_id'] == user_id].iloc[0]
# 提取embedding向量
embedding_cols = [col for col in method_df.columns if 'dim_' in col]
target_vector = user_embedding[embedding_cols].values
# 计算与所有其他用户的相似度(使用余弦相似度)
similarities = []
for idx, row in method_df.iterrows():
if row['user_id'] == user_id:
continue
other_vector = row[embedding_cols].values
# 计算余弦相似度
similarity = np.dot(target_vector, other_vector) / (
np.linalg.norm(target_vector) * np.linalg.norm(other_vector)
)
similarities.append((row['user_id'], similarity))
# 按相似度排序
similarities.sort(key=lambda x: x[1], reverse=True)
# 返回最相似的用户
print(f"\n与 {user_id} 最相似的 {n_similar} 个用户:")
for i, (similar_user, similarity) in enumerate(similarities[:n_similar], 1):
print(f"{i}. {similar_user} (相似度: {similarity:.4f})")
return similarities[:n_similar]
# 运行主程序
if __name__ == "__main__":
# 运行主函数
results = main()
# 演示如何查找相似用户
print("\n" + "="*60)
print("相似用户查找演示")
print("="*60)
# 使用第一个用户作为示例
sample_user = results['df_users'].iloc[0]['user_id']
find_similar_users(sample_user, results['all_embeddings'], method='pca', n_similar=5)
print("\n" + "="*60)
print("程序执行完成!")
print("="*60)
print("\n生成了三种不同类型的embedding:")
print("1. PCA Embedding: 基于线性降维")
print("2. 自编码器Embedding: 基于无监督深度学习")
print("3. 监督学习Embedding: 基于有监督任务学习")
print("\n所有embedding已保存到 'user_embeddings.csv' 文件中。")
代码说明
1. 数据生成部分
- 模拟了1000个用户,每个用户有100个特征
- 包含多种数据类型:数值型、类别型
- 特征包括:人口统计、行为特征、偏好特征、设备特征、社交特征
- 创建了模拟的标签(高价值用户 vs 普通用户)
2. 三种embedding生成方法
- PCA方法:简单的线性降维
- 自编码器方法:无监督深度学习,学习数据的压缩表示
- 监督学习方法:基于分类任务学习有语义的embedding
3. 主要功能
- 数据预处理和标准化
- 模型训练和评估
- 可视化训练过程和embedding
- 评估embedding质量
- 查找相似用户
- 保存embedding到CSV文件
4. 运行方式
直接运行脚本即可:
bash
python user_embedding_generator.py
5. 输出结果
- 训练过程可视化
- embedding散点图可视化
- embedding质量评估报告
- 保存的CSV文件包含所有embedding
6. 扩展使用
你可以:
- 修改生成的数据量或特征数
- 调整embedding维度
- 添加更多embedding生成方法
- 使用真实数据替换模拟数据
- 将embedding用于推荐系统、用户分群等下游任务
这个完整代码可以直接运行,展示了从数据生成到embedding应用的全过程。你可以根据实际需求调整参数和模型架构。