学习理论:代理损失函数的泛化界与Rademacher复杂度
档棕彝盟编码器-解码器encoder-decoder架构???前面的文章中我们的模型示例都是根据已有的文字序列续写N个字。在自然语言处理中还有有一类需求也是比较经典那就是机器翻译。???对于机器翻译来说其核心就是将一种语言翻译为另外一种语言换句话说就是一种序列数据到另外一种序列数据。从这里来看出现了两种序列数据那么必然的很容易想到类似两个RNN的独立网络来处理这种任务基于这种情况有人提出了编码器-解码器架构下图是这种架构的示意图。rep_img??从示意图可知这种架构的核心就是处理输入序列得到中间状态将中间状态传给解码器解码器负责生成输出序列。对于翻译任务来说输入序列就是原文输出序列就是译文。??这里说起来还是概念性的我们下面从一个经典的编码器、解码器结构的模型来实际 演示一下翻译需求的模型是什么样子的。基于 seq2seq 的 英文翻译中文 的实例??英文中文翻译数据集?? 首先数据集下载地址是http://www.manythings.org/anki/ 中的cmn-eng.zip 文件其内部的数据集格式大概如下I try. 我试试。 CC-BY 2.0 (France) Attribution: tatoeba.org #20776 (CK) #8870261 (will66)I won! 我赢了。 CC-BY 2.0 (France) Attribution: tatoeba.org #2005192 (CK) #5102367 (mirrorvan)Oh no! 不会吧。 CC-BY 2.0 (France) Attribution: tatoeba.org #1299275 (CK) #5092475 (mirrorvan)Cheers! 乾杯! CC-BY 2.0 (France) Attribution: tatoeba.org #487006 (human600) #765577 (Martha)Got it? 知道了没有 CC-BY 2.0 (France) Attribution: tatoeba.org #455353 (CM) #455357 (GlossaMatik)Got it? 懂了吗 CC-BY 2.0 (France) Attribution: tatoeba.org #455353 (CM) #2032276 (ydcok)Got it? 你懂了吗 CC-BY 2.0 (France) Attribution: tatoeba.org #455353 (CM) #7768205 (jiangche)He ran. 他跑了。 CC-BY 2.0 (France) Attribution: tatoeba.org #672229 (CK) #5092389 (mirrorvan)?? 由于我的卡3060 12G有点拉库为了效率因此整个数据集只用前面2千条即可。文本预处理# dataset.pyimport collectionsimport torchfrom torch.utils import data# 下面返回的数据是# [[Hi., 嗨。], [Hi., 你好。], [Run., 你用跑的。], [Stop!, 住手], [Wait!, 等等], ... ...]def read_data():with open(cmn-eng/cmn.txt, r,encodingutf-8) as f:lines f.readlines()return [line.split( )[:2] for line in lines]# 输出是# [[Hi.], [Hi.], [Run.], [Stop!], [Wait!]]# [[嗨, 。], [你, 好, 。], [你, 用, 跑, 的, 。], [住, 手, ], [等, 等, ]]# [Hi., Hi., Run., Stop!, Wait!]# [嗨。, 你好。, 你用跑的。, 住手, 等等]def tokenize(lines, tokenchar): #save将文本行拆分为单词或字符词元source_tokenize, target_tokenize [], []source_line, target_line [], []print(fdataset len {len(lines)})for line in lines:s line[0]t line[1]source_line.append(s)target_line.append(t)source_tokenize.append(s.split( ))target_tokenize.append([word for word in t])return source_tokenize, target_tokenize, source_line, target_line# 词元的类型是字符串而模型需要的输入是数字因此这种类型不方便模型使用。 现在让我们构建一个字典# 通常也叫做词表vocabulary 用来将字符串类型的词元映射到从开始的数字索引中。def count_corpus(tokens): #save统计词元的频率# 这里的tokens是1D列表或2D列表if len(tokens) 0 or isinstance(tokens[0], list):# 将词元列表展平成一个列表tokens [token for line in tokens for token in line]return collections.Counter(tokens)# 返回类似{l: 3, o: 2, h: 1, e: 1, : 1, w: 1, r: 1, d: 1}的一个字典class Vocab:文本词表def __init__(self, tokensNone, min_freq0, reserved_tokensNone):if tokens is None:tokens []if reserved_tokens is None:reserved_tokens []# 按出现频率排序# 对于Counter(hello world)结果如下# Counter({l: 3, o: 2, h: 1, e: 1, : 1, w: 1, r: 1, d: 1})counter count_corpus(tokens)self._token_freqs sorted(counter.items(), keylambda x: x[1],reverseTrue)# 未知词元的索引为0self.idx_to_token [] reserved_tokensself.token_to_idx {token: idxfor idx, token in enumerate(self.idx_to_token)}for token, freq in self._token_freqs:if freq min_freq:breakif token not in self.token_to_idx:self.idx_to_token.append(token)self.token_to_idx[token] len(self.idx_to_token) - 1def __len__(self):return len(self.idx_to_token)def __getitem__(self, tokens):if not isinstance(tokens, (list, tuple)):return self.token_to_idx.get(tokens, self.unk)return [self.__getitem__(token) for token in tokens]def to_tokens(self, indices):if not isinstance(indices, (list, tuple)):return self.idx_to_token[indices]return [self.idx_to_token[index] for index in indices]propertydef unk(self): # 未知词元的索引为0return 0propertydef token_freqs(self):return self._token_freqsdef truncate_pad(line, num_steps, padding_token):截断或填充文本序列if len(line) num_steps:return line[:num_steps] # 截断return line [padding_token] * (num_steps - len(line)) # 填充def build_array(lines, vocab, num_steps):将机器翻译的文本序列转换成小批量lines [vocab[l] for l in lines] # 每行的token转换为其idlines [l [vocab[]] for l in lines] # 每行的token后加上eos的idarray torch.tensor([truncate_pad(l, num_steps, vocab[]) for l in lines])valid_len (array ! vocab[]).type(torch.int32).sum(1)return array, valid_lendef load_array(data_arrays, batch_size, is_trainTrue):构造一个PyTorch数据迭代器Defined in :numref:sec_linear_concisedataset data.TensorDataset(*data_arrays)return data.DataLoader(dataset, batch_size, shuffleis_train)def load_data(batch_size, num_steps, num_examples600):返回翻译数据集的迭代器和词表text read_data()source, target, src_line, tgt_line tokenize(text)# 返回类似{l: 3, o: 2, h: 1, e: 1, : 1, w: 1, r: 1, d: 1}的一个字典src_vocab Vocab(source, min_freq0,reserved_tokens[, , ])tgt_vocab Vocab(target, min_freq0,reserved_tokens[, , ])# 首先把每行的词转换为了其对应的id然后给每一行的末尾添加token , 然后根据num_steps如果line长度不足补如果长度超出截断# 一种类型的输出是# [# [line0-char0-id, line0-char1-id, line0-char2-id, ...., eos-id],# [line1-char0-id, line1-char1-id, line1-char2-id, ...., eos-id], 注意最后的末尾可能没有eos# .....# ]src_array, src_valid_len build_array(source, src_vocab, num_steps)tgt_array, tgt_valid_len build_array(target, tgt_vocab, num_steps)data_arrays (src_array, src_valid_len, tgt_array, tgt_valid_len)data_iter load_array(data_arrays, batch_size)return data_iter, src_vocab, tgt_vocab, src_line, tgt_line??上面代码做了如下事情根据数据集的格式读取每一行只提前每行前面2个字符串。然后我们对每一行进行文字切割得到了一个二维列表列表中的每一行又被分割为一个个中文文字和一个个英文的词也就得到了一个个token。(特别注意站在当前的时刻这里的token和现在主流的大语言模型的token概念是一样的但是不是一样的实现。)由于模型不能直接处理文字我们需要将文字转换为数字那么直接的做法就是将一个个token编号即可这个时候我们得到了词表vocabulary。然后我们根据我们得到的词表对原始数据集进行数字化得到一个列表列表中每个元素就是一个个token对应的索引。最后得到基于pytorch的DataLoader、原文词表、译文词表、原文文字列表、译文文字列表??此外在这里出现了几个在后面的大语言模型中也会出现的词BOS/EOS。这两个分别代表一次对话的起始、结尾这里直接记住就行。搭建seq2seq训练框架??首先引用一些包和一些辅助classimport osimport randomimport torchimport mathfrom torch import nnfrom torch.nn import functional as Fimport numpy as npimport timeimport visdomimport collectionsimport datasetclass Accumulator:在n个变量上累加def __init__(self, n):Defined in :numref:sec_softmax_scratchself.data [0.0] * ndef add(self, *args):self.data [a float(b) for a, b in zip(self.data, args)]def reset(self):self.data [0.0] * len(self.data)def __getitem__(self, idx):return self.data[idx]class Timer:记录多次运行时间def __init__(self):Defined in :numref:subsec_linear_modelself.times []self.start()def start(self):启动计时器self.tik time.time()def stop(self):停止计时器并将时间记录在列表中self.times.append(time.time() - self.tik)return self.times[-1]def avg(self):返回平均时间return sum(self.times) / len(self.times)def sum(self):返回时间总和return sum(self.times)def cumsum(self):返回累计时间return np.array(self.times).cumsum().tolist()??然后我们根据编码器、解码器架构设计seq2seq的网络主干class Encoder(nn.Module):编码器-解码器架构的基本编码器接口def __init__(self, **kwargs):# 调用父类nn.Module的构造函数确保正确初始化super(Encoder, self).__init__(**kwargs)def forward(self, X, *args):# 抛出未实现错误意味着该方法需要在子类中具体实现raise NotImplementedErrorclass Decoder(nn.Module):编码器-解码器架构的基本解码器接口Defined in :numref:sec_encoder-decoderdef __init__(self, **kwargs):# 调用父类nn.Module的构造函数确保正确初始化super(Decoder, self).__init__(**kwargs)def init_state(self, enc_outputs, *args):# 抛出未实现错误意味着该方法需要在子类中具体实现raise NotImplementedErrordef forward(self, X, state):# 抛出未实现错误意味着该方法需要在子类中具体实现raise NotImplementedErrorclass EncoderDecoder(nn.Module):编码器-解码器架构的基类Defined in :numref:sec_encoder-decoderdef __init__(self, encoder, decoder, **kwargs):# 调用父类nn.Module的构造函数确保正确初始化super(EncoderDecoder, self).__init__(**kwargs)# 将传入的编码器实例赋值给类的属性self.encoder encoder# 将传入的解码器实例赋值给类的属性self.decoder decoderdef forward(self, enc_X, dec_X, *args):# 调用编码器的前向传播方法处理输入的编码器输入数据enc_Xenc_outputs self.encoder(enc_X, *args)# 调用解码器的init_state方法根据编码器的输出初始化解码器的状态dec_state self.decoder.init_state(enc_outputs, *args)# 调用解码器的前向传播方法处理输入的解码器输入数据dec_X和初始化后的状态return self.decoder(dec_X, dec_state)#saveclass Seq2SeqEncoder(Encoder):用于序列到序列学习的循环神经网络编码器def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,dropout0, **kwargs):super(Seq2SeqEncoder, self).__init__(**kwargs)# 嵌入层self.embedding nn.Embedding(vocab_size, embed_size)self.rnn nn.GRU(embed_size, num_hiddens, num_layers,dropoutdropout)# self.lstm nn.LSTM(embed_size, num_hiddens, num_layers)def forward(self, X, *args):# 输入X.shape (batch_size,num_steps)# 输出X的形状(batch_size,num_steps,embed_size)X self.embedding(X)# 在循环神经网络模型中第一个轴对应于时间步X X.permute(1, 0, 2)# 如果未提及状态则默认为0output, state self.rnn(X)# output : 这个返回值是所有时间步的隐藏状态序列# output的形状:(num_steps,batch_size,num_hiddens)# hn (hidden) : 这是每一层rnn的最后一个时间步的隐藏状态# state的形状:(num_layers,batch_size,num_hiddens)return output, stateclass Seq2SeqDecoder(Decoder):用于序列到序列学习的循环神经网络解码器def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,dropout0, **kwargs):super(Seq2SeqDecoder, self).__init__(**kwargs)self.embedding nn.Embedding(vocab_size, embed_size)self.rnn nn.GRU(embed_size num_hiddens, num_hiddens, num_layers,dropoutdropout)self.dense nn.Linear(num_hiddens, vocab_size)def init_state(self, enc_outputs, *args):return enc_outputs[1]def forward(self, X, state):# 输出X的形状(batch_size,num_steps,embed_size)X self.embedding(X).permute(1, 0, 2)# 广播context使其具有与X相同的num_stepscontext state[-1].repeat(X.shape[0], 1, 1)X_and_context torch.cat((X, context), 2)output, state self.rnn(X_and_context, state)output self.dense(output).permute(1, 0, 2)# output的形状:(batch_size,num_steps,vocab_size)# state的形状:(num_layers,batch_size,num_hiddens)return output, state??我们结合上面的架构图对比着看首先声明一下decoder/encoder的接口类声明了Encoder(nn.Module)Encoder(nn.Module)其输入是原文输出是中间状态。声明了Decoder(nn.Module)Decoder(nn.Module)的输入是BOS和中间状态输出是译文。声明了EncoderDecoder(nn.Module)类串联Encoder(nn.Module)/Decoder(nn.Module)进行运行。??然后声明实际的Seq2SeqEncoder部分声明了Seq2SeqEncoder(Encoder)其是seq2seq编码器部分的实际定义其输入是一串原文然后经过了nn.Embedding将输入的token序列转换为token-embedding然后送入nn.GRU得到了两个值最后一层rnn的所有时间步的隐藏状态outputshapenum_steps,batch_size,num_hiddens所有层rnn的最后一个时间步的隐藏状态h_nshapenum_layers,batch_size,num_hiddens从Seq2SeqEncoder(Encoder)上面的分析可知rnn的输出output代表的是每一个时间步当前序列的总结信息h_n encoder的隐藏态参数。??最后声明实际的Seq2SeqDecoder部分声明了Seq2SeqDecoder(Decoder)输入是一个是bos一个是Seq2SeqEncoder(Encoder)输出的隐藏态state(output,h_n)。首先将bos转换为embedding向量然后将h_n的最后一个数据也就是原文的总结rnn最后一层最后一个时间步的隐藏态和embedding组合在一起(注意这里已经将原文的语义已经和bos输入混合在一起了)和Seq2SeqEncoder(Encoder) state作为的隐藏状态初始值一起传入rnn然后经过nn.Linear的映射得到了decoder的输出。从Seq2SeqDecoder(Decoder)的分析可知经过了nn.Linear映射之后我们将decoder层的rnn的output转换为词表大小的一个向量这个向量我们可以看做下一个字的分数Logits注意这个概念在后续大语言模型中有比较大的作用。??这里nn.RNN等pytorch层的输出可以结合下面这个图来理解图来自于参考文献相关链接rep_img?? 下面给出的就是训练、预测部分的代码def try_gpu(i0):如果存在则返回gpu(i)否则返回cpu()Defined in :numref:sec_use_gpuif torch.cuda.device_count() i 1:return torch.device(fcuda:{i})return torch.device(cpu)def sequence_mask(X, valid_len, value0):在序列中屏蔽不相关的项maxlen X.size(1)mask torch.arange((maxlen), dtypetorch.float32,deviceX.device)[None, :] valid_len[:, None]X[~mask] valuereturn Xclass MaskedSoftmaxCELoss(nn.CrossEntropyLoss):带遮蔽的softmax交叉熵损失函数# pred的形状(batch_size,num_steps,vocab_size)# label的形状(batch_size,num_steps)# valid_len的形状(batch_size,)def forward(self, pred, label, valid_len):weights torch.ones_like(label)weights sequence_mask(weights, valid_len)self.reductionnoneunweighted_loss super(MaskedSoftmaxCELoss, self).forward(pred.permute(0, 2, 1), label)weighted_loss (unweighted_loss * weights).mean(dim1)return weighted_lossdef grad_clipping(net, theta): #save裁剪梯度if isinstance(net, nn.Module):params [p for p in net.parameters() if p.requires_grad]else:params net.paramsnorm torch.sqrt(sum(torch.sum((p.grad ** 2)) for p in params))if norm theta:for param in params:param.grad[:] * theta / normdef train_seq2seq(net, data_iter, lr, num_epochs, tgt_vocab, device):训练序列到序列模型def xavier_init_weights(m):if type(m) nn.Linear:nn.init.xavier_uniform_(m.weight)if type(m) nn.GRU:for param in m._flat_weights_names:if weight in param:nn.init.xavier_uniform_(m._parameters[param])net.apply(xavier_init_weights)net.to(device)optimizer torch.optim.Adam(net.parameters(), lrlr)loss MaskedSoftmaxCELoss()net.train()vis visdom.Visdom(envutest1, serverhttp://127.0.0.1, port8097)animator visfor epoch in range(num_epochs):timer Timer()metric Accumulator(2) # 训练损失总和词元数量for batch in data_iter:#清零reset优化器中的梯度缓存optimizer.zero_grad()# x.shape [batch_size, num_steps]X, X_valid_len, Y, Y_valid_len [x.to(device) for x in batch]# bos.shape batch_size 个 bos-idbos torch.tensor([tgt_vocab[]] * Y.shape[0],devicedevice).reshape(-1, 1)# dec_input.shape (batch_size, num_steps)# 解码器的输入通常由序列的起始标志 bos 和目标序列去掉末尾的部分 Y[:, :-1]组成。dec_input torch.cat([bos, Y[:, :-1]], 1) # 强制教学# Y_hat的形状:(batch_size,num_steps,vocab_size)Y_hat, _ net(X, dec_input, X_valid_len)l loss(Y_hat, Y, Y_valid_len)l.sum().backward() # 损失函数的标量进行“反向传播”grad_clipping(net, 1)num_tokens Y_valid_len.sum()optimizer.step()with torch.no_grad():metric.add(l.sum(), num_tokens)if (epoch 1) % 10 0:# print(predict(你是))# print(epoch)# animator.add(epoch 1, )if epoch 9:# 清空图表使用空数组来替换现有内容vis.line(Xnp.array([0]), Ynp.array([0]), wintrain_ch8, updatereplace)# _loss_val l# _loss_val _loss_val.cpu().sum().detach().numpy()vis.line(Xnp.array([epoch 1]),Y[ metric[0] / metric[1]],wintrain_ch8,updateappend,opts{title: train_ch8,xlabel: epoch,ylabel: loss,linecolor: np.array([[0, 0, 255]]), # 蓝色线条})print(floss {metric[0] / metric[1]:.3f}, {metric[1] / timer.stop():.1f} ftokens/sec on {str(device)})torch.save(net.cpu().state_dict(), model_h.pt) # [[6]]torch.save(net.cpu(), model.pt) # [[6]]def predict_seq2seq(net, src_sentence, src_vocab, tgt_vocab, num_steps,device, save_attention_weightsFalse):序列到序列模型的预测# 在预测时将net设置为评估模式net.eval()src_tokens src_vocab[src_sentence.lower().split( )] [src_vocab[]]enc_valid_len torch.tensor([len(src_tokens)], devicedevice)src_tokens dataset.truncate_pad(src_tokens, num_steps, src_vocab[])# 添加批量轴enc_X torch.unsqueeze(torch.tensor(src_tokens, dtypetorch.long, devicedevice), dim0)enc_outputs net.encoder(enc_X, enc_valid_len)dec_state net.decoder.init_state(enc_outputs, enc_valid_len)# 添加批量轴dec_X torch.unsqueeze(torch.tensor([tgt_vocab[]], dtypetorch.long, devicedevice), dim0)output_seq, attention_weight_seq [], []for _ in range(num_steps):Y, dec_state net.decoder(dec_X, dec_state)# 我们使用具有预测最高可能性的词元作为解码器在下一时间步的输入dec_X Y.argmax(dim2)pred dec_X.squeeze(dim0).type(torch.int32).item()# 保存注意力权重稍后讨论if save_attention_weights:attention_weight_seq.append(net.decoder.attention_weights)# 一旦序列结束词元被预测输出序列的生成就完成了if pred tgt_vocab[]:breakoutput_seq.append(pred)return .join(tgt_vocab.to_tokens(output_seq)), attention_weight_seqdef bleu(pred_seq, label_seq, k): #save计算BLEUpred_tokens, label_tokens pred_seq.split( ), [i for i in label_seq]len_pred, len_label len(pred_tokens), len(label_tokens)score math.exp(min(0, 1 - len_label / len_pred))for n in range(1, k 1):num_matches, label_subs 0, collections.defaultdict(int)for i in range(len_label - n 1):label_subs[ .join(label_tokens[i: i n])] 1for i in range(len_pred - n 1):if label_subs[ .join(pred_tokens[i: i n])] 0:num_matches 1label_subs[ .join(pred_tokens[i: i n])] - 1score * math.pow(num_matches / (len_pred - n 1), math.pow(0.5, n))return score??这里首先要介绍一下其损失函数核心两个通过交叉熵计算真实分布、预测分布的差异性差异性越小意味着我们的模型越好由于我们是序列模型可能涉及pad项这些pad项的位置是无意义的但是对模型有影响我们需要再loss中剔除掉这种无意义的位置我们用mask来屏蔽。??然后训练过程的核心就是从数据集中获取 训练数据、验证数据通过训练数据得到预测数据预测数据和验证数据进行loss计算然后进行反向传播找到loss最小化的方向然后最小化loss模型就会越来越好。??然后就是介绍预测部分的内容先将原文输入到seq的encoder然后将bos序列 seq的encoder的隐藏态传给seq的decoder就可以得到下一个字的输出直到我们遇到eos预测结束。??我们虽然预测完毕了得到了原文对应的译文但是我们需要一种方法来评估我们翻译的是不是正确这里用的方法是bleu它的作用就是评估输出序列与目标序列的精确度。??最后我们开始训练过程注意下面的例子是先进行训练然后保存pt模型然后加载模型进行预测推理。if __name__ __main__:embed_size, num_hiddens, num_layers, dropout 32, 32, 2, 0.1batch_size, num_steps 64, 10lr, num_epochs, device 0.005, 2000, try_gpu()# train_iter 每个迭代输出(batch_size, num_steps)train_iter, src_vocab, tgt_vocab, source, target dataset.load_data(batch_size, num_steps)encoder Seq2SeqEncoder(len(src_vocab), embed_size, num_hiddens, num_layers,dropout)decoder Seq2SeqDecoder(len(tgt_vocab), embed_size, num_hiddens, num_layers,dropout)net EncoderDecoder(encoder, decoder)is_train Falseif is_train:train_seq2seq(net, train_iter, lr, num_epochs, tgt_vocab, device)else:state_dict torch.load(model_h.pt)net.load_state_dict(state_dict)net.to(device)C 0C1 0for i in range(2000):# print(source[i])# print(target[i])translation, attention_weight_seq predict_seq2seq(net, source[i], src_vocab, tgt_vocab, num_steps, device)score bleu(translation, target[i], k2)if score 0.0:C C 1if score 0.8:C1 C1 1print(f{source[i]} {translation}, bleu {score:.3f})print(fCounter(bleu 0) {C})print(fValid-Counter(bleu 0.8) {C1})rep_imgrep_img??从上面的图可以看到这个模型有一定的翻译效果。??此外我这里计算了非零的bleu以及大于0.8的bleu的个数这个个数勉强可以评估我们对现在这个seq2seq模型优化的效果为后面的文章提前做一些准备工作。

相关新闻

AI赋能大数据开发:从初级Chat到高级Agent,全面解析数据工程师提效秘籍!

AI赋能大数据开发:从初级Chat到高级Agent,全面解析数据工程师提效秘籍!

前言 AI 对大数据开发的变革是全流程、智能化的,从数据采集到业务洞察的每个环节,都在通过自动化、优化、智能决策提升效率和价值。 对于大数据开发工程师而言,掌握 AI 与大数据的结合点,不仅能提升自身工作效率,更能…

2026/7/5 21:43:51 阅读更多 →
C++ 模板参数推导问题小记(非推导上下文)

C++ 模板参数推导问题小记(非推导上下文)

宰荡艘鞍嗯。const,顾名思义,就是不变。给任何数据类型加上 const,就指明了这个变量不会再变化。任何试图修改变量的操作都会报错,无法通过编译。比如: const int a 10; a 11; //Error! 当然,常量也必须在…

2026/7/6 18:18:40 阅读更多 →
通过Certbot自动申请更新HTTPS网站的SSL证书

通过Certbot自动申请更新HTTPS网站的SSL证书

际憾婪松通过 start.spring.io 或其他构建基础的Spring Boot工程,根据你构建应用的需要增加一些依赖,比如最后用接口提供服务的话,可以加入web模块:org.springframework.bootspring-boot-starter-web2. 注册 UnifiedTTS&#xff0…

2026/7/5 18:45:52 阅读更多 →

最新新闻

PyTorch EMA实现避坑:decay=0.999与0.9999的4个关键差异点

PyTorch EMA实现避坑:decay=0.999与0.9999的4个关键差异点

PyTorch EMA实现深度解析:从数学原理到工业级避坑指南在深度学习模型的训练过程中,噪声和波动是影响最终性能的关键因素。指数移动平均(EMA)作为一种简单却强大的技术,能够有效平滑模型权重更新轨迹,提升模…

2026/7/7 6:25:46 阅读更多 →
本地部署AI生图与视频生成工具:免费无限制完整指南

本地部署AI生图与视频生成工具:免费无限制完整指南

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 这次我们来深入分析一款本地部署的AI生图和视频生成工具。从标题看,这款工具主打免费无限制、功能超越小云雀和即梦2.0等知…

2026/7/7 6:25:46 阅读更多 →
计算机毕业设计之jsp企业舆情监控与分析系统

计算机毕业设计之jsp企业舆情监控与分析系统

“互联网”的战略实施后,很多行业的信息化水平都有了很大的提升。但是目前很多企业日常工作仍是通过人工管理的方式进行,需要在各个岗位投入大量的人力进行很多重复性工作,这样就浪费了许多的人力物力,工作效率较低,同…

2026/7/7 6:25:46 阅读更多 →
RFID手持终端与固定式读写器在资产盘点场景中的协同组网策略

RFID手持终端与固定式读写器在资产盘点场景中的协同组网策略

在企业固定资产、仓储物资、机房设备等资产管控场景中,传统人工盘点存在效率低、误差大、资产溯源难、账实不符频发等痛点,难以适配现代化企业海量、分散、动态的资产管控需求。RFID射频识别技术凭借非接触式批量读取、远距离识别、数据实时同步的核心优…

2026/7/7 6:25:46 阅读更多 →
Okbiye AI 写作|分档适配全等级期刊论文,一站式搞定普刊、核心、SCI 文稿创作

Okbiye AI 写作|分档适配全等级期刊论文,一站式搞定普刊、核心、SCI 文稿创作

okbiye-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/期刊论文期刊论文 - Okbiye智能写作https://www.okbiye.com/ai/qklw 对于高校研究生、青年科研工作者来说,期刊投稿是学术成长路上绕不开的关卡。想发普通期刊完成毕业指标,深耕中文核…

2026/7/7 6:23:45 阅读更多 →
3.1 数据治理思想的演进:从数据库管理到数据要素战略的四十年来路

3.1 数据治理思想的演进:从数据库管理到数据要素战略的四十年来路

3.1 数据治理思想的演进:从数据库管理到数据要素战略的四十年来路3.1.1 演进全景图:四十年四次跃迁3.1.2 1980s-1990s:数据库管理时代——数据建模与存储优化时代背景核心关注点对治理思想的贡献历史局限与痛点3.1.3 2000s:数据仓…

2026/7/7 6:21:45 阅读更多 →

日新闻

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

图片浏览是移动应用中最高频的场景之一。从社交应用的照片流到电商平台的商品图集,从旅游应用的景点相册到摄影作品展示——用户对图片浏览的体验要求不断提高:流畅的切换动画、直观的缩略图导航、便捷的收藏操作、自动播放模式。HarmonyOS NEXT ArkUI 虽…

2026/7/7 0:05:16 阅读更多 →
24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V稳压芯片完整选型指南 PW8600 PW75XX PW2815 PW2312B LDODC/DC全方案 一、24V稳压方案概述 24V直流电源在工业自动化、门禁系统、电梯控制、汽车电子、LED驱动、监控设备等场景中应用极广,是最常见的中压直流母线电压。要将24V母线稳定降压至下游MCU、传感器…

2026/7/7 0:05:16 阅读更多 →
RAG+知识图谱混合检索与Graph RAG核心对比

RAG+知识图谱混合检索与Graph RAG核心对比

做企业RAG落地的团队,往往容易卡在一容易踩坑的选型难题: 当需求单纯靠向量RAG搞不定、单纯靠知识图谱也搞不定,必须同时依赖「文本语义理解 实体关系推理」时,到底是做「向量图谱混合检索」就够了,还是必须上「Grap…

2026/7/7 0:07:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻