You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyTorch中基于矩阵分解与自定义损失的自定义优化实现验证

论文《Recommendation for Effective Standardized Exam Preparation》复现校验与替代实现

核心实现逻辑校验(帮你确认PyTorch代码正确性)

先对照以下关键要点检查你的实现:

  • 修正sigmoid函数φ:论文中为φ(z) = 1/(1+exp(-α(z-β))),注意不是标准sigmoid,需确认α、β参数是否按论文设定(通常α=1、β=0.5,或按论文给定值)。
  • 约束处理:
    • 学生隐概念矩阵L的元素必须被钳制在[0,1],可通过torch.clamp或在参数初始化后强制约束;
    • 题目隐概念矩阵R需满足每行和为1且元素∈[0,1]:每次SGD更新后,先对R做元素钳制,再按行做L1归一化(除以行求和)。
  • 损失函数:需包含两部分:
    1. 二元交叉熵(BCE)损失:基于φ(L@R^T)与真实答题矩阵X的计算;
    2. Frobenius范数正则化:对L和R的范数平方求和,乘以正则系数λ。
  • SGD优化:确保参数更新后立即应用上述约束,避免违反论文设定的参数范围。

其他可行实现方法

1. TensorFlow/Keras实现

适合熟悉TF生态的场景,可利用自动微分与训练流程封装:

import tensorflow as tf

# 配置参数
num_students = 100
num_items = 50
num_concepts = 20
lambda_reg = 0.01
alpha = 1.0
beta = 0.5
lr = 0.01
epochs = 1000

# 初始化可训练变量
L = tf.Variable(tf.random.uniform((num_students, num_concepts), 0, 1))
R = tf.Variable(tf.random.uniform((num_items, num_concepts), 0, 1))
# 初始归一化R的行
R.assign(tf.divide(R, tf.reduce_sum(R, axis=1, keepdims=True)))

# 修正sigmoid函数
def modified_sigmoid(z):
    return 1 / (1 + tf.exp(-alpha * (z - beta)))

# 定义损失函数
def compute_loss(X_true):
    pred = modified_sigmoid(tf.matmul(L, tf.transpose(R)))
    bce_loss = tf.reduce_mean(tf.keras.losses.binary_crossentropy(X_true, pred))
    reg_loss = lambda_reg * (tf.norm(L, ord='fro')**2 + tf.norm(R, ord='fro')**2)
    return bce_loss + reg_loss

# SGD优化器
optimizer = tf.optimizers.SGD(learning_rate=lr)

# 训练循环
@tf.function
def train_step(X_true):
    with tf.GradientTape() as tape:
        loss = compute_loss(X_true)
    grads = tape.gradient(loss, [L, R])
    optimizer.apply_gradients(zip(grads, [L, R]))
    
    # 应用约束
    L.assign(tf.clip_by_value(L, 0, 1))
    R_clamped = tf.clip_by_value(R, 0, 1)
    R.assign(tf.divide(R_clamped, tf.reduce_sum(R_clamped, axis=1, keepdims=True)))
    return loss

# 模拟训练数据
X = tf.random.uniform((num_students, num_items), 0, 2, dtype=tf.int32)
X = tf.cast(X, tf.float32)

for epoch in range(epochs):
    loss = train_step(X)
    if epoch % 100 == 0:
        print(f"Epoch {epoch:4d} | Loss: {loss:.4f}")

2. 纯NumPy手动实现SGD

适合深入理解底层梯度计算逻辑,无框架依赖:

import numpy as np

# 配置参数
num_students = 100
num_items = 50
num_concepts = 20
lambda_reg = 0.01
alpha = 1.0
beta = 0.5
lr = 0.01
epochs = 1000

# 修正sigmoid与损失计算
def modified_sigmoid(z):
    return 1 / (1 + np.exp(-alpha * (z - beta)))

def compute_loss(X, L, R):
    pred = modified_sigmoid(L @ R.T)
    epsilon = 1e-8  # 避免log(0)
    bce = -np.mean(X * np.log(pred + epsilon) + (1 - X) * np.log(1 - pred + epsilon))
    reg = lambda_reg * (np.linalg.norm(L, 'fro')**2 + np.linalg.norm(R, 'fro')**2)
    return bce + reg

# 手动计算梯度
def compute_gradients(X, L, R):
    pred = modified_sigmoid(L @ R.T)
    error = pred - X
    phi_prime = alpha * pred * (1 - pred)  # 修正sigmoid的导数
    grad_L = (error * phi_prime) @ R + 2 * lambda_reg * L
    grad_R = ((error * phi_prime).T @ L) + 2 * lambda_reg * R
    return grad_L, grad_R

# 初始化参数并约束R
L = np.random.uniform(0, 1, (num_students, num_concepts))
R = np.random.uniform(0, 1, (num_items, num_concepts))
R = R / R.sum(axis=1, keepdims=True)

# 模拟训练数据
X = np.random.randint(0, 2, (num_students, num_items)).astype(np.float32)

# SGD训练循环
for epoch in range(epochs):
    grad_L, grad_R = compute_gradients(X, L, R)
    # 更新参数
    L -= lr * grad_L
    R -= lr * grad_R
    # 应用约束
    L = np.clip(L, 0, 1)
    R = np.clip(R, 0, 1)
    R = R / R.sum(axis=1, keepdims=True)
    # 打印进度
    if epoch % 100 == 0:
        loss = compute_loss(X, L, R)
        print(f"Epoch {epoch:4d} | Loss: {loss:.4f}")

3. Scikit-learn风格自定义模型

方便集成到sklearn的机器学习流水线中:

from sklearn.base import BaseEstimator, RegressorMixin
import numpy as np

class ExamMatrixFactorizer(BaseEstimator, RegressorMixin):
    def __init__(self, num_concepts=20, alpha=1.0, beta=0.5, lambda_reg=0.01, lr=0.01, epochs=1000):
        self.num_concepts = num_concepts
        self.alpha = alpha
        self.beta = beta
        self.lambda_reg = lambda_reg
        self.lr = lr
        self.epochs = epochs
        self.L_ = None
        self.R_ = None
    
    def modified_sigmoid(self, z):
        return 1 / (1 + np.exp(-self.alpha * (z - self.beta)))
    
    def compute_loss(self, X):
        pred = self.modified_sigmoid(self.L_ @ self.R_.T)
        epsilon = 1e-8
        bce = -np.mean(X * np.log(pred + epsilon) + (1 - X) * np.log(1 - pred + epsilon))
        reg = self.lambda_reg * (np.linalg.norm(self.L_, 'fro')**2 + np.linalg.norm(self.R_, 'fro')**2)
        return bce + reg
    
    def fit(self, X, y=None):
        num_students, num_items = X.shape
        # 初始化参数
        self.L_ = np.random.uniform(0, 1, (num_students, self.num_concepts))
        self.R_ = np.random.uniform(0, 1, (num_items, self.num_concepts))
        self.R_ = self.R_ / self.R_.sum(axis=1, keepdims=True)
        
        for epoch in range(self.epochs):
            pred = self.modified_sigmoid(self.L_ @ self.R_.T)
            error = pred - X
            phi_prime = self.alpha * pred * (1 - pred)
            # 计算梯度
            grad_L = (error * phi_prime) @ self.R_ + 2 * self.lambda_reg * self.L_
            grad_R = ((error * phi_prime).T @ self.L_) + 2 * self.lambda_reg * self.R_
            # 更新参数
            self.L_ -= self.lr * grad_L
            self.R_ -= self.lr * grad_R
            # 应用约束
            self.L_ = np.clip(self.L_, 0, 1)
            self.R_ = np.clip(self.R_, 0, 1)
            self.R_ = self.R_ / self.R_.sum(axis=1, keepdims=True)
            
            if epoch % 100 == 0:
                loss = self.compute_loss(X)
                print(f"Epoch {epoch:4d} | Loss: {loss:.4f}")
        return self
    
    def predict(self, X=None):
        # 返回所有学生-题目的预测正确率
        return self.modified_sigmoid(self.L_ @ self.R_.T)

内容的提问来源于stack exchange,提问作者Deshwal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 04:25:06