news 2026/9/28 18:29:50

基于图神经网络的文本分类:TextGCN、TextING与LEAM复现指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于图神经网络的文本分类:TextGCN、TextING与LEAM复现指南

简介:这份资源是面向计算机、人工智能及相关专业学生的NLP期末大作业完整实现,聚焦TextGCN、TextING、LEAM三种文本分类模型的Python复现,适合课程设计、毕业设计或项目入门参考。压缩包共91个文件,约805.69MB,以32个py源码为核心,辅以ipynb实验笔记、pdf说明文档、TensorFlow checkpoint与data分片等模型数据文件,以及npz、npy等预处理特征,目录按TextGCN、TextING、LEAM三个子项目组织,各自包含建图、预处理、模型定义与训练脚本,结构清晰便于对照学习。目前已有438人学习下载。代码均经测试运行成功,并配有详细注释,读者可据此理解图神经网络与词嵌入在文本分类中的实现细节,掌握从语料构建、图结构生成到模型训练评估的完整流程,也可在此基础上修改以适配其他分类任务或作为答辩演示材料。

1. 从三个模型看文本分类的图神经网络落地路径

做 NLP 自然语言处理期末大作业时,选题往往比写代码更让人头疼。图像分类可以堆 ResNet,目标检测可以调 YOLO,但文本分类一旦脱离 BERT 微调,很多人就不知道还能做什么。TextGCN、TextING、LEAM 这三个模型恰好构成了一条从「图卷积」到「图注意力」再到「标签嵌入」的完整技术链路,覆盖了文本分类中异构图建模、词级交互、标签语义利用三个核心问题。它们不是最新的 SOTA,但胜在结构清晰、代码量可控、复现难度适中,非常适合作为 NLP 期末大作业的骨架。这篇文章会从数据准备、模型搭建、训练调参到排错,把三个模型的复现路径完整走一遍,目标是让你拿到一份能跑通、能改、能写进报告的实现方案。适合已经学过 PyTorch 基础、想找一个有技术深度但又不至于无从下手的 NLP 项目的同学。

2. TextGCN:把整篇语料建成一张异构图

2.1 为什么文本分类可以变成图节点分类

TextGCN 的核心思路非常直接:把整个语料库里的所有文档和所有词都当作节点,文档-词共现关系当作边,文档-文档、词-词之间的 PMI 关系也当作边,然后在这个异构图上面跑两层 GCN,最后取文档节点的表示做分类。这个思路在 2019 年提出时之所以有冲击力,是因为它完全抛弃了 CNN/RNN 的序列建模方式,把文本分类转化成了图上的半监督节点分类问题。

具体来说,假设你有 N 篇文档、M 个词,那么图里一共有 N+M 个节点。边分三种:文档-词边(TF-IDF 值)、词-词边(PMI 值)、文档-文档边(PMI 值)。构建完图之后,用两层 GCN 做消息传递,文档节点的最终嵌入直接送进 softmax 分类器。整个过程不需要预训练词向量,也不需要复杂的特征工程,图的拓扑结构本身就编码了全局共现信息。

这个方案适合什么场景?语料规模中等(几千到几万篇)、类别边界依赖关键词共现、标注数据少但无标注数据多的情况。如果你的数据是短文本、口语化严重、或者类别区分主要靠语序而非词汇共现,TextGCN 的效果会打折扣。

2.2 构图脚本:从原始文本到邻接矩阵

下面这段代码完成从原始文本到图邻接矩阵的转换。输入是一个包含text和label两列的 DataFrame,输出是稀疏邻接矩阵、特征矩阵和标签数组。

import numpy as np import pandas as pd from sklearn.feature_extraction.text import CountVectorizer from sklearn.preprocessing import LabelEncoder from scipy.sparse import csr_matrix, lil_matrix import math def build_textgcn_graph(df, max_vocab=5000, window_size=20, pmi_threshold=0.0): """ 构建 TextGCN 异构图 df: 包含 text 和 label 列的 DataFrame max_vocab: 词表最大容量 window_size: 词共现窗口大小 pmi_threshold: PMI 边过滤阈值 """ # 1. 构建词表 vectorizer = CountVectorizer(max_features=max_vocab, token_pattern=r'(?u)\b\w+\b') doc_word_matrix = vectorizer.fit_transform(df['text']) vocab = vectorizer.get_feature_names_out() vocab_size = len(vocab) doc_num = len(df) total_nodes = doc_num + vocab_size # 2. 标签编码 le = LabelEncoder() labels = le.fit_transform(df['label']) # 3. 构建文档-词边(TF-IDF 权重) tf = doc_word_matrix.toarray().astype(float) doc_freq = (tf > 0).sum(axis=0) idf = np.log(doc_num / (doc_freq + 1)) tfidf = tf * idf # 归一化 row_sum = tfidf.sum(axis=1, keepdims=True) row_sum[row_sum == 0] = 1 tfidf = tfidf / row_sum # 4. 构建词-词 PMI 边 # 统计滑动窗口内的共现 cooccur = np.zeros((vocab_size, vocab_size)) word2idx = {w: i for i, w in enumerate(vocab)} for text in df['text']: words = [w for w in text.split() if w in word2idx] for i, w in enumerate(words): for j in range(i + 1, min(i + window_size, len(words))): wi, wj = word2idx[w], word2idx[words[j]] cooccur[wi][wj] += 1 cooccur[wj][wi] += 1 # 计算 PMI word_sum = cooccur.sum() word_freq = cooccur.sum(axis=1) pmi = np.zeros_like(cooccur) for i in range(vocab_size): for j in range(vocab_size): if cooccur[i][j] > 0: p_ij = cooccur[i][j] / word_sum p_i = word_freq[i] / word_sum p_j = word_freq[j] / word_sum pmi[i][j] = max(math.log(p_ij / (p_i * p_j + 1e-9)), 0) # 5. 组装全局邻接矩阵 adj = lil_matrix((total_nodes, total_nodes)) # 文档-词边 for d in range(doc_num): for w in range(vocab_size): if tfidf[d][w] > 0: adj[d, doc_num + w] = tfidf[d][w] adj[doc_num + w, d] = tfidf[d][w] # 词-词边 for i in range(vocab_size): for j in range(vocab_size): if pmi[i][j] > pmi_threshold: adj[doc_num + i, doc_num + j] = pmi[i][j] # 文档-文档边(通过共享词计算 PMI,此处简化为共享词数归一化) doc_word_binary = (tf > 0).astype(float) doc_doc = doc_word_binary @ doc_word_binary.T doc_doc = doc_doc / (doc_doc.max() + 1e-9) for i in range(doc_num): for j in range(i + 1, doc_num): if doc_doc[i][j] > 0: adj[i, j] = doc_doc[i][j] adj[j, i] = doc_doc[i][j] # 6. 对称归一化 adj = csr_matrix(adj) degree = np.array(adj.sum(axis=1)).flatten() degree[degree == 0] = 1 d_inv_sqrt = np.power(degree, -0.5) d_mat = csr_matrix(np.diag(d_inv_sqrt)) adj_norm = d_mat @ adj @ d_mat # 7. 特征矩阵:文档节点用 one-hot,词节点用 one-hot features = np.eye(total_nodes) return adj_norm, features, labels, le.classes_, vocab

这段代码的逻辑链条是:先分词建词表,再算 TF-IDF 构建文档-词边,然后通过滑动窗口统计共现并计算 PMI 构建词-词边,最后用共享词比例构建文档-文档边。参数方面,max_vocab控制图规模,太大显存扛不住,太小信息损失严重,5000 到 10000 是常见区间;window_size影响词-词边的密度,英文数据集常用 20,中文短文本可以降到 5 到 10;pmi_threshold用来过滤弱关联边,设 0 表示保留所有正 PMI 边,如果图太密可以调到 0.5 到 1.0。

2.3 两层 GCN 的前向传播与训练循环

图构建好之后,模型部分反而简单。两层 GCN 的公式是H = softmax(A_norm @ ReLU(A_norm @ X @ W1) @ W2),其中A_norm是归一化邻接矩阵,X是单位矩阵(每个节点用 one-hot 表示)。

import torch import torch.nn as nn import torch.nn.functional as F class TextGCN(nn.Module): def __init__(self, num_nodes, hidden_dim, num_classes, dropout=0.5): super().__init__() self.gc1 = nn.Linear(num_nodes, hidden_dim, bias=False) self.gc2 = nn.Linear(hidden_dim, num_classes, bias=False) self.dropout = dropout def forward(self, adj, features): # 第一层:A @ X @ W1 x = torch.sparse.mm(adj, features) if adj.is_sparse else adj @ features x = self.gc1(x) x = F.relu(x) x = F.dropout(x, self.dropout, training=self.training) # 第二层:A @ H @ W2 x = torch.sparse.mm(adj, x) if adj.is_sparse else adj @ x x = self.gc2(x) return F.log_softmax(x, dim=1) # 训练循环 def train_textgcn(adj, features, labels, train_mask, val_mask, test_mask, hidden_dim=200, lr=0.01, weight_decay=5e-4, epochs=200): device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') adj = adj.to(device) features = features.to(device) labels = torch.LongTensor(labels).to(device) model = TextGCN(features.shape[0], hidden_dim, len(set(labels.cpu().numpy()))).to(device) optimizer = torch.optim.Adam(model.parameters(), lr=lr, weight_decay=weight_decay) best_val_acc = 0 for epoch in range(epochs): model.train() optimizer.zero_grad() out = model(adj, features) loss = F.nll_loss(out[train_mask], labels[train_mask]) loss.backward() optimizer.step() # 验证 model.eval() with torch.no_grad(): out = model(adj, features) pred = out.argmax(dim=1) val_acc = (pred[val_mask] == labels[val_mask]).float().mean().item() if val_acc > best_val_acc: best_val_acc = val_acc test_acc = (pred[test_mask] == labels[test_mask]).float().mean().item() if epoch % 20 == 0: print(f'Epoch {epoch}, Loss: {loss.item():.4f}, Val Acc: {val_acc:.4f}') return test_acc

这里有几个关键点。第一,nn.Linear(num_nodes, hidden_dim, bias=False)看起来参数量巨大(节点数乘以隐藏维度),但因为输入是 one-hot,实际计算等价于查表,PyTorch 的稀疏矩阵乘法会自动优化。第二,weight_decay设 5e-4 是原文推荐值,对 GCN 的过拟合抑制很重要。第三,训练时只在train_mask上算损失,验证和测试节点不参与梯度更新,这是半监督学习的标准做法。如果显存不够,可以把features转成稀疏张量,或者减小hidden_dim到 64 到 128。

3. TextING:用词级图注意力捕捉局部交互

3.1 TextING 和 TextGCN 的本质区别

TextGCN 把整篇语料建成一张大图,每个文档是一个节点,文档内部的词序信息完全丢失。TextING 换了一个思路:每篇文档单独建一张词级图,词是节点,词与词之间的共现关系是边,然后用 GAT 在每篇文档的图上做消息传递,最后把词节点表示聚合成文档表示。这样做的好处是保留了文档内部的局部结构,而且每篇文档的图是独立的,可以批量处理。

TextING 的核心创新在于它用了两种边:一种是基于滑动窗口的共现边,另一种是基于 TF-IDF 的全局边。两种边分别用不同的注意力头处理,最后拼接。这个设计让模型既能捕捉局部搭配关系,又能利用全局重要性信息。

从复现难度看,TextING 比 TextGCN 稍高,因为需要为每篇文档动态建图,但代码结构更接近常规的 PyTorch 训练流程,调试起来反而更直观。

3.2 文档级词图的构建与批处理

TextING 的图构建是 per-document 的,所以需要写一个 Dataset 类,在__getitem__里动态建图。

from torch.utils.data import Dataset from torch_geometric.data import Data, Batch class TextINGDataset(Dataset): def __init__(self, texts, labels, vocab, max_len=200, window_size=3): self.texts = texts self.labels = labels self.vocab = vocab self.max_len = max_len self.window_size = window_size def __len__(self): return len(self.texts) def __getitem__(self, idx): words = self.texts[idx].split()[:self.max_len] word_ids = [self.vocab.get(w, 0) for w in words] n = len(word_ids) # 节点特征:词嵌入将在模型中查表,这里先用 one-hot 索引 x = torch.LongTensor(word_ids) # 构建边:滑动窗口共现 edge_index = [] edge_attr = [] for i in range(n): for j in range(i + 1, min(i + self.window_size + 1, n)): edge_index.append([i, j]) edge_index.append([j, i]) # 边权重用 1/(距离) 表示 w = 1.0 / (j - i) edge_attr.append(w) edge_attr.append(w) if len(edge_index) == 0: edge_index = [[0, 0]] edge_attr = [0.0] edge_index = torch.LongTensor(edge_index).t().contiguous() edge_attr = torch.FloatTensor(edge_attr) y = torch.LongTensor([self.labels[idx]]) return Data(x=x, edge_index=edge_index, edge_attr=edge_attr, y=y, num_nodes=n) def collate_texting(batch): """自定义批处理:把多篇文档的图合并成一个大图""" return Batch.from_data_list(batch)

这段代码的关键在于edge_attr的设计。TextING 原文用了两种边权重:一种是共现频率,一种是 TF-IDF 相似度。这里简化为距离倒数,实际复现时可以根据报告需要替换。max_len控制每篇文档的最大词数,太长会导致图太大、训练慢,200 是一个折中值。window_size设 3 表示每个词只和前后 3 个词连边,这个参数对短文本分类影响很大,建议在验证集上从 2 到 5 之间扫一遍。

3.3 GAT 层的实现与文档表示聚合

TextING 的模型部分需要自定义 GAT 层,因为要同时利用边权重和节点特征。

import torch import torch.nn as nn import torch.nn.functional as F from torch_geometric.nn import MessagePassing from torch_geometric.utils import softmax class EdgeWeightedGAT(MessagePassing): def __init__(self, in_dim, out_dim, heads=4, dropout=0.3): super().__init__(aggr='add') self.heads = heads self.out_dim = out_dim self.lin = nn.Linear(in_dim, out_dim * heads, bias=False) self.att = nn.Parameter(torch.Tensor(1, heads, 2 * out_dim)) self.edge_lin = nn.Linear(1, heads, bias=False) self.dropout = dropout self.reset_parameters() def reset_parameters(self): nn.init.xavier_uniform_(self.lin.weight) nn.init.xavier_uniform_(self.att) nn.init.xavier_uniform_(self.edge_lin.weight) def forward(self, x, edge_index, edge_attr, num_nodes): x = self.lin(x).view(-1, self.heads, self.out_dim) return self.propagate(edge_index, x=x, edge_attr=edge_attr, num_nodes=num_nodes) def message(self, x_i, x_j, edge_attr, index): # 拼接源节点和目标节点 alpha = torch.cat([x_i, x_j], dim=-1) # [E, heads, 2*out] alpha = (alpha * self.att).sum(dim=-1) # [E, heads] alpha = F.leaky_relu(alpha, 0.2) # 加入边权重偏置 edge_bias = self.edge_lin(edge_attr.unsqueeze(-1)) # [E, heads] alpha = alpha + edge_bias alpha = softmax(alpha, index, num_nodes=num_nodes) alpha = F.dropout(alpha, self.dropout, training=self.training) return x_j * alpha.unsqueeze(-1) class TextING(nn.Module): def __init__(self, vocab_size, embed_dim=100, hidden_dim=64, num_classes=5, heads=4, dropout=0.3): super().__init__() self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0) self.gat1 = EdgeWeightedGAT(embed_dim, hidden_dim, heads, dropout) self.gat2 = EdgeWeightedGAT(hidden_dim * heads, hidden_dim, heads, dropout) self.classifier = nn.Linear(hidden_dim * heads, num_classes) self.dropout = dropout def forward(self, data): x, edge_index, edge_attr = data.x, data.edge_index, data.edge_attr x = self.embedding(x) x = F.dropout(x, self.dropout, training=self.training) x = F.elu(self.gat1(x, edge_index, edge_attr, data.num_nodes)) x = F.dropout(x, self.dropout, training=self.training) x = F.elu(self.gat2(x, edge_index, edge_attr, data.num_nodes)) # 文档表示:对每篇文档的词节点做平均池化 batch = data.batch doc_repr = torch.zeros(batch.max() + 1, x.size(-1), device=x.device) doc_repr = doc_repr.index_add(0, batch, x) counts = torch.zeros(batch.max() + 1, device=x.device).index_add_(0, batch, torch.ones_like(batch, dtype=torch.float)) doc_repr = doc_repr / counts.unsqueeze(-1) return self.classifier(doc_repr)

这里的EdgeWeightedGAT在标准 GAT 的基础上增加了edge_lin,把标量边权重映射成每个注意力头的偏置。message函数里的softmax(alpha, index, num_nodes=num_nodes)是 PyG 提供的按目标节点归一化的 softmax,比手动实现更稳定。文档表示聚合用index_add按 batch 分组求平均,这是处理变长图批次的常用技巧。训练时用Batch.from_data_list把多篇文档的图合并,data.batch会自动标记每个节点属于哪篇文档。

4. LEAM:把标签也嵌入到同一个空间

4.1 标签嵌入为什么能提升分类效果

LEAM 的全称是 Label Embedding Attentive Model,核心思想是把标签也当作可学习的嵌入向量,然后计算每个词和每个标签的相似度,用这个相似度作为注意力权重来聚合词表示。这样做的好处是模型在训练过程中会主动拉近词表示和对应标签表示的距离,相当于一种隐式的对比学习。

和 TextGCN、TextING 相比,LEAM 的结构最简单,没有图结构,就是一个注意力池化层加一个分类层。但它的效果在多个基准数据集上都不差,尤其是在类别语义区分明显的场景下。复现 LEAM 的难点不在模型本身,而在标签嵌入的初始化和注意力温度系数的调节。

4.2 标签嵌入注意力层的代码实现

import torch import torch.nn as nn import torch.nn.functional as F class LEAM(nn.Module): def __init__(self, vocab_size, embed_dim=200, num_classes=5, hidden_dim=128, dropout=0.5, temperature=1.0): super().__init__() self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0) self.label_embedding = nn.Embedding(num_classes, embed_dim) self.temperature = temperature self.dropout = dropout self.classifier = nn.Sequential( nn.Linear(embed_dim, hidden_dim), nn.ReLU(), nn.Dropout(dropout), nn.Linear(hidden_dim, num_classes) ) self._init_weights() def _init_weights(self): nn.init.xavier_uniform_(self.embedding.weight) nn.init.xavier_uniform_(self.label_embedding.weight) def forward(self, input_ids, attention_mask=None): # input_ids: [B, L] word_emb = self.embedding(input_ids) # [B, L, D] label_emb = self.label_embedding.weight # [C, D] # 计算词-标签相似度 # [B, L, D] x [D, C] -> [B, L, C] similarity = torch.matmul(word_emb, label_emb.t()) / self.temperature attention = F.softmax(similarity, dim=1) # 对词维度归一化 # 用注意力权重聚合词表示 # [B, L, C] x [B, L, D] -> [B, C, D] doc_repr = torch.einsum('blc,bld->bcd', attention, word_emb) # 对每个标签的文档表示分别分类 # 这里简化:把所有标签的表示拼接后分类 doc_repr = doc_repr.mean(dim=1) # [B, D] doc_repr = F.dropout(doc_repr, self.dropout, training=self.training) logits = self.classifier(doc_repr) return logits

这段代码的核心是torch.einsum('blc,bld->bcd', attention, word_emb)。它的含义是:对于每个样本、每个标签,用该标签对所有词的注意力权重对词嵌入做加权求和,得到一个标签特定的文档表示。然后对所有标签的表示取平均,送进分类器。temperature控制注意力的尖锐程度,值越小注意力越集中,值越大越平滑。实践中 0.5 到 2.0 之间调节,太小容易过拟合,太大注意力退化成平均池化。

4.3 三个模型的训练配置对比与选型建议

把三个模型放在一起对比,能更清楚地看到各自的适用边界。

维度TextGCNTextINGLEAM
图结构全局异构图文档级词图无图
参数量大(节点数×隐藏维度)中(词嵌入+GAT)小(词嵌入+标签嵌入)
训练速度慢(稀疏矩阵乘法)中(批处理图)快
显存占用高中低
适合语料长文本、类别靠共现短文本、局部搭配重要类别语义区分明显
调参难度中(构图参数多)高(图构建+GAT)低
复现优先级先跑通其次最后

选型建议:如果期末大作业时间紧,先复现 LEAM,一天能跑通,效果也不差;如果报告需要体现技术深度,TextGCN 的构图部分可以写很多分析;如果想让报告有创新点,TextING 的边权重设计和注意力可视化是很好的素材。三个模型不需要全部从头实现,可以共用一套数据加载和评估流程,只替换模型部分。

5. 复现三个模型时最容易翻车的五个地方

5.1 现象:TextGCN 训练 loss 不下降,准确率卡在多数类

原因:邻接矩阵没有做对称归一化,或者归一化时 degree 计算用了错误的轴。另一个常见原因是特征矩阵用了 TF-IDF 而不是 one-hot,导致第一层 GCN 的输入尺度不一致。

解决:检查adj_norm = D^{-1/2} A D^{-1/2}是否按行计算 degree。用adj.sum(axis=1)而不是axis=0。特征矩阵保持单位矩阵,不要替换成词向量,TextGCN 原文就是这么设计的。

5.2 现象:TextING 的 batch 里不同文档的图合并后,节点索引错乱

原因:Batch.from_data_list会自动给每个图的edge_index加上偏移量,但如果手动拼接图而没有更新edge_index,就会出现跨文档的边。

解决:始终用 PyG 的Batch类做批处理,不要手动torch.cat。如果必须手动拼接,记得累加节点偏移:edge_index += offset,其中offset是前面所有图的节点总数。

5.3 现象:LEAM 的标签嵌入在训练初期梯度爆炸

原因:标签嵌入和词嵌入共享同一个空间,但初始化尺度不一致,导致相似度计算出现极端值。

解决:对label_embedding和embedding都用xavier_uniform_初始化,并且在相似度计算后加temperature缩放。如果仍然不稳定,可以对相似度做clamp(-10, 10)截断。

5.4 现象:三个模型在中文数据集上效果普遍比英文差 10 个点以上

原因:中文没有空格分词,直接用split()会把整句当成一个词。另外中文的词汇共现模式和英文差异很大,PMI 阈值需要重新调。

解决:用 jieba 分词后再构建词表。TextGCN 的window_size从 20 降到 5 到 8。TextING 的max_len从 200 降到 100 到 150。LEAM 的词嵌入维度可以从 200 降到 100,因为中文词表通常比英文小。

5.5 现象:验证集准确率远高于测试集,差距超过 15 个点

原因:数据划分时没有按类别分层采样,导致验证集和测试集的类别分布不一致。另一个原因是 TextGCN 的图构建用了全部数据(包括测试集),造成信息泄露。

解决:用sklearn.model_selection.train_test_split的stratify参数按标签分层。TextGCN 构图时只用训练集的文档和词,验证集和测试集的文档节点在图中保留但边不参与训练。如果做不到,至少在报告里说明这是 transductive 设定。

6. 用一个小技巧把三个模型的报告串起来

复现完三个模型之后,报告最容易写成三个独立实验的拼凑。我的习惯是找一个统一的评估维度把它们串起来。具体做法是:在同一个数据集上,固定训练集/验证集/测试集划分,分别跑三个模型,然后画一张「准确率 vs 训练时间」的散点图,再附一张「不同类别数下的准确率变化」折线图。

代码上只需要一个统一的评估函数:

def evaluate_model(model, dataloader, device): model.eval() all_preds, all_labels = [], [] with torch.no_grad(): for batch in dataloader: batch = batch.to(device) logits = model(batch) preds = logits.argmax(dim=1).cpu().numpy() all_preds.extend(preds) all_labels.extend(batch.y.cpu().numpy()) from sklearn.metrics import accuracy_score, f1_score acc = accuracy_score(all_labels, all_preds) f1 = f1_score(all_labels, all_preds, average='macro') return acc, f1

这个函数对三个模型都适用,只要把batch的格式统一成包含x、edge_index、y的Data对象。TextGCN 需要把全局图包装成一个Data,LEAM 可以把input_ids和attention_mask放进Data的自定义属性里。

最后分享一个血泪经验:三个模型不要并行复现,先跑通 LEAM,用它验证数据加载和评估流程;再把 LEAM 的 Dataset 改造成 TextING 的图 Dataset;最后用 TextGCN 替换模型部分。这样每一步只改一个变量,出问题容易定位。如果一上来就三个同时写,最后连 loss 不下降都不知道是数据问题还是模型问题。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 18:29:17

仿射变换矩阵入门:用 TaoToken 统一 Key 跑通图像配准最小示例

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 18:28:28

开关电源输出纹波示波器测试设置规范

前言文件版本:V1.0编制目的:明确电源输出纹波测试中交流耦合(AC档)、20MHz带宽限制的定义、原理与执行要求,统一测试条件,保证测试数据可对比、可复现适用范围:DC-DC、开关电源模块、板载电源输…

作者头像 李华
网站建设 2026/9/28 18:27:14

实战:为 Agent Harness 接入 TaoToken 语音交互配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 18:26:22

【全网最全横评】8家大厂8只AI龙虾Agent实测对比:OpenClaw、AutoClaw、KimiClaw、QClaw谁才是最优解?TaoToken统一Key接入实测

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华