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

如何用梯度下降优化余弦相似度?0-1权重向量组合求解

问题分析与解决方案

原代码核心问题

  1. 禁用Eager Execution:TensorFlow 1.x图模式下,所有操作都是张量节点,直接打印只会输出结构,无法获取实际数值,这是看不到变量具体值的根本原因。
  2. 变量管理混乱:每次循环重新创建tf.Variable,导致梯度无法在迭代中持续传递,训练完全无效。
  3. 离散操作不可导:tf.round是离散操作,梯度为0,梯度下降无法更新变量,必须用连续松弛方法。
  4. 低效的手动循环:手动遍历维度计算B,既慢又容易出错,应该用矩阵运算替代。
  5. 除以0风险:加权平均时若所有权重为0,sum(variables)为0会触发错误。

可行实现思路

这是一个组合优化问题(选择C的子集,加权平均后与A的余弦相似度最大),直接用梯度下降处理离散0/1变量不可行,需采用连续松弛策略:

  1. 将权重定义为可训练的连续变量,用sigmoid函数约束在[0,1]区间。
  2. 基于连续权重优化损失(1 - 余弦相似度),最大化A与B的相似度等价于最小化该损失。
  3. 训练完成后,将连续权重四舍五入为0/1,得到离散解。
  4. 用Eager Execution(TensorFlow 2.x默认开启),方便实时查看变量数值和训练过程。

修正后的代码

import tensorflow as tf
import numpy as np

# 向量维度
d = 1508
# 目标向量A(转为TensorFlow张量)
A = tf.convert_to_tensor(np.random.uniform(0, 1, size=d), dtype=tf.float32)
# 向量列表C(转为形状为[n, d]的张量)
n = 4
C = tf.convert_to_tensor(np.random.uniform(0, 1, size=(n, d)), dtype=tf.float32)

# 定义可训练的连续权重变量,初始值设为0.5
weights = tf.Variable(tf.ones(n, dtype=tf.float32) * 0.5, trainable=True)

# 优化器选择Adam(比SGD更适合这类问题)
optimizer = tf.keras.optimizers.Adam(learning_rate=0.1)

num_steps = 500
for step in range(num_steps):
    with tf.GradientTape() as tape:
        # 用sigmoid将权重约束在[0,1]区间
        continuous_weights = tf.sigmoid(weights)
        # 计算加权和:(n,) @ (n,d) -> (d,)
        weighted_sum = tf.tensordot(continuous_weights, C, axes=1)
        # 计算权重和,加极小值避免除以0
        sum_weights = tf.reduce_sum(continuous_weights) + 1e-8
        # 得到加权平均向量B
        B = weighted_sum / sum_weights
        # 计算损失:1 - 余弦相似度(最小化损失等价于最大化相似度)
        loss = 1 - tf.keras.losses.cosine_similarity(A, B)
    
    # 计算梯度并更新权重
    grads = tape.gradient(loss, weights)
    optimizer.apply_gradients([(grads, weights)])
    
    # 每50步打印一次训练状态
    if step % 50 == 0:
        print(f"Step {step}, Loss: {loss.numpy():.4f}")
        print(f"Current continuous weights: {continuous_weights.numpy()}")

# 训练完成后,将连续权重四舍五入为0/1
discrete_weights = tf.round(continuous_weights).numpy()
print("\nFinal discrete weights (0/1):", discrete_weights)

# 验证最终B与A的余弦相似度
final_weighted_sum = tf.tensordot(discrete_weights, C, axes=1)
final_sum_weights = tf.reduce_sum(discrete_weights) + 1e-8
final_B = final_weighted_sum / final_sum_weights
final_similarity = 1 - loss(A, final_B).numpy()
print(f"Final cosine similarity between A and B: {final_similarity:.4f}")

关键说明

  1. Eager Execution:默认开启,所有操作实时计算,通过.numpy()方法可以直接获取张量的数值。
  2. 连续松弛:用sigmoid(weights)替代直接0/1变量,保证梯度可导,训练后再通过tf.round得到离散解。
  3. 矩阵运算:用tf.tensordot替代手动循环,大幅提升计算效率,尤其适合高维度向量。
  4. 边界处理:加1e-8避免权重和为0时的除以0错误。
  5. 变量管理:只定义一次tf.Variable,迭代中持续更新,保证梯度传递有效。

额外提示

如果n较小(比如n<20),也可以用暴力枚举法遍历所有2^n种可能的权重组合,直接计算每种组合下的余弦相似度,选出最优解,这种方法结果绝对最优,不需要梯度下降。但n较大时,必须用启发式方法(如连续松弛+梯度下降)近似求解。

内容的提问来源于stack exchange,提问作者Constantly Groovin'

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:05:38