论文:
1.首先利用llm从普通的描述中获取结构化信息
利用llm来生成与类别相关的描述以及相应的结构化关系;
笑鼠,代码还没看,光看论文觉得太复杂了,引入Q矩阵(e2e和e2a)entity实体。attribute属性。
1.使用结构化知识指导学习得到的基于注意力的矩阵(attention-based matrices),并将这些矩阵集成到文本编码器的不同层中。
在实践中,通过同时计算一组查询(queries)上的注意力函数来实现这一点。这些查询被打包成一个矩阵Q。类似地,键(keys)和值(values)也被分别打包成矩阵K和V。
对于第l层,使用基于注意力的矩阵MlMl,自注意力的输出通过以下公式计算:
这里,Ml是为每一层特别设计的矩阵,用于显式表示该层内每个描述的丰富结构关系,从而增强与类别相关的关键信息。
2.层次化的知识建模方法,将来自多个层次的整体语义与结构化关系相结合,有助于发现大型语言模型(LLMs)未能识别的复杂关联。
-
整合结构化知识:通过在自注意力机制中引入额外的矩阵Ml,可以更好地捕捉输入数据中的结构化信息,这有助于提高模型对于特定任务的表现。
-
跨层级自注意力:这种方法允许模型自动学习不同抽象层次之间的关系,无需人为定义这些关系。它通过结合高层次和全局级别的提示,能够捕捉长程依赖性和复杂的语义联系。
-
增强表达能力:通过上述机制,模型不仅能够理解单个词或短语的意义,还能捕捉整个句子或段落内部以及跨句子、跨段落之间的复杂关系,从而提升对文本深层次含义的理解能力。
该技术通过在自注意力机制中融入结构化知识和跨层级提示,增强了模型理解和表达复杂文本关系的能力。
反正很复杂,看不懂......
250223
迁移:主要是学生不同
或者是知识点不同
如果二者都不一样,那没办法搞啊
阅读文章:
Prompt-to-prompt:让生成的图像保持一致 - 知乎
理了下PromptCD代码:
publisher-PromptCD/PromptCD: PromptCD:传输跨域认知诊断的提示
以irt模型为例:
1.初始化irt模型
初始化中
self.pp_dim = pp_dim # 提示向量维度
self.latent_dim = latent_dim # 潜在特征维度
self.s_irt_net = Source_MIRTNet( # 源域模型
user_num, s_item_num, latent_dim,
pp_dim, s_ranges, a_range
)
self.t_irt_net = Target_MIRTNet( # 目标域模型(版本1)
user_num, t_item_num, latent_dim,
pp_dim, s_ranges, a_range
)
self.t_irt_net2 = Target_MIRTNet2( # 目标域模型(初始化有优化)
user_num, t_item_num, latent_dim,
pp_dim, s_ranges, a_range
)
class Source_MIRTNet(nn.Module):
def __init__(self, user_num, item_num, latent_dim, pp_dim, s_ranges, a_range, irf_kwargs=None):
super(Source_MIRTNet, self).__init__()
self.user_num = user_num
self.item_num = item_num
self.pp_dim = pp_dim
self.s_ranges = s_ranges
self.latent_dim = latent_dim
self.irf_kwargs = irf_kwargs if irf_kwargs is not None else {}
self.b = nn.Parameter(torch.rand((self.item_num, self.latent_dim)))
nn.init.xavier_uniform_(self.b)
self.s_exer_vectors = nn.ParameterList([nn.Parameter(torch.rand(self.pp_dim)) for _ in range(len(s_ranges))])
self.theta = nn.ParameterList([nn.Parameter(torch.randn(self.user_num, self.latent_dim))
for _ in range(len(s_ranges))])
for theta in self.theta:
nn.init.xavier_uniform_(theta)
self.prompt_theta = nn.Parameter(torch.randn(self.user_num, self.pp_dim))
nn.init.xavier_uniform_(self.prompt_theta)
self.a = nn.Embedding(self.item_num, 1)
self.c = nn.Embedding(self.item_num, 1)
self.a_range = 1
self.value_range = 8
self.fc1 = nn.Linear(self.pp_dim + self.latent_dim, self.latent_dim)
self.fc2 = nn.Linear(self.pp_dim + self.latent_dim, self.latent_dim)
def forward(self, user, item):
prompt_theta_repeated = self.prompt_theta.repeat(len(self.s_ranges), 1)
theta_concatenated = torch.cat([theta for theta in self.theta], dim=0)
new_theta = torch.cat([prompt_theta_repeated, theta_concatenated], dim=1)
new_theta = torch.index_select(new_theta, dim=0, index=user)
new_theta = self.fc1(new_theta)
new_theta = torch.squeeze(new_theta, dim=-1)
temp_vectors = torch.cat(
[vector.repeat(r[1] - r[0] + 1, 1) for vector, r in zip(self.s_exer_vectors, self.s_ranges)], dim=0)
all_b = torch.cat([temp_vectors, self.b], dim=1)
new_b = torch.index_select(all_b, dim=0, index=item)
new_b = self.fc2(new_b)
new_b = torch.squeeze(new_b, dim=-1)
new_a = self.a(item)
new_a = torch.squeeze(new_a, dim=-1)
new_c = torch.squeeze(self.c(item), dim=-1)
new_c = torch.sigmoid(new_c)
if self.value_range is not None:
new_theta = self.value_range * (torch.sigmoid(new_theta) - 0.5)
new_b = self.value_range * (torch.sigmoid(new_b) - 0.5)
if self.a_range is not None:
new_a = self.a_range * torch.sigmoid(new_a)
else:
new_a = F.softplus(new_a)
#------------------------------
if torch.max(new_theta != new_theta) or torch.max(new_a != new_a) or torch.max(new_b != new_b): # pragma: no cover
raise ValueError('ValueError:theta,a,b may contains nan! The a_range is too large.')
return self.irf(new_theta, new_a, new_b, new_c, **self.irf_kwargs)
class Source_MIRTNet(nn.Module):
def __init__(self, user_num, item_num, latent_dim, pp_dim, s_ranges, a_range, irf_kwargs=None):
super(Source_MIRTNet, self).__init__()
# 基础参数
self.user_num = user_num # 用户总数
self.item_num = item_num # 题目总数(源域)
self.pp_dim = pp_dim # 提示向量维度(用于迁移学习)
self.s_ranges = s_ranges # 源域题目范围划分(如知识点分类)
self.latent_dim = latent_dim # 潜在特征维度(用户能力θ和题目难度b的维度)
self.irf_kwargs = irf_kwargs # IRT模型参数(如三参数模型配置)
self.a_range = a_range # 区分度a的范围约束(如[0,1])
self.value_range = 8 # θ和b的范围约束(如[-4,4])
题目参数:(主要看题目范围参数,每个范围一个pp_dim维向量)
# 题目难度参数(b):每个题目对应一个latent_dim维向量
self.b = nn.Parameter(torch.rand((self.item_num, self.latent_dim)))
nn.init.xavier_uniform_(self.b) # Xavier初始化
# 题目范围嵌入向量(s_exer_vectors):每个范围一个pp_dim维向量
self.s_exer_vectors = nn.ParameterList([
nn.Parameter(torch.rand(self.pp_dim))
for _ in range(len(s_ranges)) # 根据s_ranges长度创建(如代数、几何各一个)
])
用户参数:
# 用户能力参数(theta):每个范围独立的能力矩阵(如代数能力、几何能力)
self.theta = nn.ParameterList([
nn.Parameter(torch.randn(self.user_num, self.latent_dim))
for _ in range(len(s_ranges))
])
for theta in self.theta: # Xavier初始化每个能力矩阵
nn.init.xavier_uniform_(theta)
# 用户提示向量(prompt_theta):跨范围共享的迁移适配参数
self.prompt_theta = nn.Parameter(torch.randn(self.user_num, self.pp_dim))
nn.init.xavier_uniform_(self.prompt_theta)
其中:torch.randn 函数用于生成一个形状为 (self.user_num, self.pp_dim) 的张量,其中的元素是从标准正态分布(均值为0,标准差为1)中随机抽取的。
self.user_num 表示用户的数量,而 self.pp_dim 则代表预处理维度或先验/后验维度的大小。这个二维张量可以看作是每个用户对应的一个向量集合,每个向量的长度为 pp_dim。
self.prompt_theta: 此变量被设计用来存储与每个用户相关的“提示”向量。这里的“提示”可能是指某种形式的上下文信息或额外特征,旨在增强模型对不同用户行为的理解能力。这些向量初始时是随机的,但随着模型的训练,它们将根据任务需求进行调整,以更好地捕捉每个用户的独特偏好或特性。
初始化:使用标准正态分布来初始化这些参数是一种常见的做法,因为它有助于打破对称性,使得神经网络中的各个神经元能够学习不同的特征,从而加速训练过程。
题目区分度与猜测函数:
self.a = nn.Embedding(self.item_num, 1) # 区分度参数a self.c = nn.Embedding(self.item_num, 1) # 猜测概率参数c
特征融合层
self.fc1 = nn.Linear(pp_dim + latent_dim, latent_dim) # 用户特征融合 self.fc2 = nn.Linear(pp_dim + latent_dim, latent_dim) # 题目特征融合
2. 前向传播逻辑解析
(1) 用户能力生成
def forward(self, user, item):
# 步骤1:扩展提示向量以匹配范围数量
prompt_theta_repeated = self.prompt_theta.repeat(len(self.s_ranges), 1)
# 示例:若s_ranges有2个范围,则每个用户的提示向量复制2次
# 步骤2:拼接所有范围的用户能力(theta_concatenated)
theta_concatenated = torch.cat([theta for theta in self.theta], dim=0)
# 结果形状:(num_ranges * user_num, latent_dim)
# 步骤3:将提示向量与用户能力拼接
new_theta = torch.cat([prompt_theta_repeated, theta_concatenated], dim=1)
# 形状:(num_ranges * user_num, pp_dim + latent_dim)
# 步骤4:根据输入的user索引选择对应的融合特征
new_theta = torch.index_select(new_theta, dim=0, index=user)
# 假设user是批量用户的索引,形状:(batch_size,)
# 步骤5:通过全连接层生成最终用户能力
new_theta = self.fc1(new_theta) # 输出形状:(batch_size, latent_dim)
new_theta = torch.squeeze(new_theta, dim=-1)
设计意图:
-
分范围能力建模:每个范围(如知识点)的用户能力独立存储,增强模型对领域内细粒度差异的捕捉。
-
提示向量融合:通过
prompt_theta
引入迁移学习能力,为后续目标域适配提供接口。
# 步骤1:生成题目范围特征(temp_vectors)
temp_vectors = torch.cat([
vector.repeat(r[1] - r[0] + 1, 1)
for vector, r in zip(self.s_exer_vectors, self.s_ranges)
], dim=0)
# 示例:若s_ranges为[(0,99), (100,199)],则每个范围向量重复100次
# 步骤2:拼接范围特征与题目基础难度b
all_b = torch.cat([temp_vectors, self.b], dim=1) # 形状:(item_num, pp_dim + latent_dim)
# 步骤3:根据输入的item索引选择题目特征
new_b = torch.index_select(all_b, dim=0, index=item) # 形状:(batch_size, pp_dim + latent_dim)
# 步骤4:通过全连接层生成最终题目难度
new_b = self.fc2(new_b) # 形状:(batch_size, latent_dim)
new_b = torch.squeeze(new_b, dim=-1)
(3) 题目参数提取与约束
# 区分度参数a
new_a = self.a(item) # 形状:(batch_size, 1)
new_a = torch.squeeze(new_a, dim=-1) # 形状:(batch_size,)
# 猜测概率参数c(通过sigmoid约束到[0,1])
new_c = torch.squeeze(self.c(item), dim=-1)
new_c = torch.sigmoid(new_c) # 形状:(batch_size,)
# 参数范围约束
if self.value_range is not None:
new_theta = self.value_range * (torch.sigmoid(new_theta) - 0.5) # [-value_range/2, value_range/2]
new_b = self.value_range * (torch.sigmoid(new_b) - 0.5) # 同上
if self.a_range is not None:
new_a = self.a_range * torch.sigmoid(new_a) # [0, a_range]
else:
new_a = F.softplus(new_a) # 确保非负
前向传播流程图——
用户输入 → [分范围theta] → 拼接 → 融合提示向量 → fc1 → 标准化 → IRF
↗ (prompt_theta)
题目输入 → [范围嵌入 + 基础b] → fc2 → 标准化 → IRF
↘ [a, c] → 标准化 → IRF
就是在target训练的时候,只改变prompt向量部分,达到利用源目标数据的作用。