你的第一個Transformer模型:從零實現并訓練一個迷你ChatBot

點擊AladdinEdu,同學們用得起的【H卡】算力平臺”,注冊即送-H卡級別算力80G大顯存按量計費靈活彈性頂級配置學生更享專屬優惠


引言:破除神秘感,擁抱核心思想

在人工智能的浪潮中,Transformer模型無疑是一顆最璀璨的明珠。從GPT系列到BERT,從翻譯到對話,它的身影無處不在。然而,對于許多初學者而言,Transformer常常被冠以“復雜”、“晦澀難懂”的標簽,厚厚的論文和錯綜復雜的結構圖讓人望而卻步。

今天,我們的目標就是親手撕掉這層神秘的面紗。我們將不使用任何高級的深度學習框架(如Hugging Face的Transformers庫),而是僅借助PyTorch提供的基礎張量操作和神經網絡模塊,從零開始,一行代碼一行代碼地構建一個完整的Transformer模型。最終,我們會在一個小型數據集上訓練它,讓它成為一個能進行簡單對話的迷你ChatBot。

相信我,當你跟著本文完成整個流程,并看到你的模型開始生成回復時,你會對Self-Attention、位置編碼等核心概念有一種“頓悟”般的感覺。這不僅是一次編程練習,更是一次深入理解現代AI核心架構的絕佳旅程。

讓我們開始吧!


第一部分:Transformer架構總覽

在深入代碼之前,我們快速回顧一下Transformer的核心設計。其最初在論文《Attention Is All You Need》中提出,完全基于Attention機制,摒棄了傳統的循環和卷積結構。

一個典型的Transformer包含一個編碼器(Encoder)和一個解碼器(Decoder)。對于我們的聊天機器人任務,編碼器負責理解輸入的問句,解碼器則負責生成輸出的答句。

  • 編碼器:由N個(原文是6個)相同的層堆疊而成。每層包含兩個子層:

    1. 多頭自注意力機制(Multi-Head Self-Attention)
    2. 前饋神經網絡(Position-wise Feed-Forward Network)
      每個子層周圍都有一個殘差連接(Residual Connection)層歸一化(Layer Normalization)
  • 解碼器:同樣由N個相同的層堆疊而成。每層包含三個子層:

    1. 掩碼多頭自注意力機制(Masked Multi-Head Self-Attention):確保解碼時只能看到當前位置之前的信息,防止“偷看”未來答案。
    2. 多頭編碼器-解碼器注意力機制(Multi-Head Encoder-Decoder Attention):幫助解碼器關注輸入序列中的相關信息。
    3. 前饋神經網絡
      同樣,每個子層也都有殘差連接和層歸一化。

此外,模型最開始有輸入嵌入層位置編碼,最后有輸出線性層Softmax

我們的代碼實現將嚴格遵循這個結構。我們將自底向上地構建它。


第二部分:核心模塊代碼實現

我們首先實現最核心、最關鍵的幾個模塊。

1. Self-Attention 與 Scaled Dot-Product Attention

Self-Attention是Transformer的靈魂。它的目的是讓序列中的任何一個字都能夠與序列中的所有其他字進行交互,從而更好地捕捉上下文信息。

Scaled Dot-Product Attention的計算公式如下:
Attention(Q,K,V)=softmax(QKTdk)VAttention(Q, K, V) = softmax(\frac{QK^T}{\sqrt{d_k}})VAttention(Q,K,V)=softmax(dk??QKT?)V

其中:

  • Q (Query):查詢矩陣,代表當前要關注的詞。
  • K (Key):鍵矩陣,代表序列中所有待被查詢的詞。
  • V (Value):值矩陣,代表序列中所有詞的實際信息。
  • d_k:Key向量的維度,縮放因子dk\sqrt{d_k}dk??用于防止點積過大導致softmax梯度消失。
import torch
import torch.nn as nn
import torch.nn.functional as F
import mathclass ScaledDotProductAttention(nn.Module):"""Scaled Dot-Product Attention"""def __init__(self, dropout_rate=0.1):super(ScaledDotProductAttention, self).__init__()self.dropout = nn.Dropout(dropout_rate)def forward(self, Q, K, V, attn_mask=None):# Q, K, V 的形狀: [batch_size, n_heads, seq_len, d_k or d_v]# d_k = d_model / n_headsd_k = K.size()[-1]# 計算注意力分數 QK^T / sqrt(d_k)scores = torch.matmul(Q, K.transpose(-1, -2)) / math.sqrt(d_k)# 如果提供了注意力掩碼,應用它(將mask為1的位置置為一個極小的值,如-1e9)if attn_mask is not None:scores = scores.masked_fill(attn_mask == 1, -1e9)# 對最后一維(seq_len維)進行softmax,得到注意力權重attn_weights = F.softmax(scores, dim=-1)# 可選:應用dropoutattn_weights = self.dropout(attn_weights)# 將注意力權重乘以V,得到最終的輸出output = torch.matmul(attn_weights, V) # [batch_size, n_heads, seq_len, d_v]return output, attn_weights

2. Multi-Head Attention

多頭注意力機制將模型分為多個“頭”,讓每個頭去關注序列中不同的方面(例如,有的頭關注語法關系,有的頭關注語義關系),最后將各頭的輸出合并起來。

class MultiHeadAttention(nn.Module):"""Multi-Head Attention mechanism"""def __init__(self, d_model, n_heads, dropout_rate=0.1):super(MultiHeadAttention, self).__init__()assert d_model % n_heads == 0, "d_model must be divisible by n_heads"self.d_model = d_modelself.n_heads = n_headsself.d_k = d_model // n_heads # 每個頭的維度self.d_v = d_model // n_heads# 線性投影層,用于生成Q, K, Vself.W_Q = nn.Linear(d_model, d_model)self.W_K = nn.Linear(d_model, d_model)self.W_V = nn.Linear(d_model, d_model)self.W_O = nn.Linear(d_model, d_model) # 輸出投影層self.attention = ScaledDotProductAttention(dropout_rate)self.dropout = nn.Dropout(dropout_rate)self.layer_norm = nn.LayerNorm(d_model)def forward(self, Q, K, V, attn_mask=None):# 殘差連接residual = Qbatch_size = Q.size(0)# 線性投影并分頭# (batch_size, seq_len, d_model) -> (batch_size, seq_len, n_heads, d_k) -> (batch_size, n_heads, seq_len, d_k)q_s = self.W_Q(Q).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)k_s = self.W_K(K).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)v_s = self.W_V(V).view(batch_size, -1, self.n_heads, self.d_v).transpose(1, 2)# 如果需要,擴展attn_mask以匹配多頭形狀if attn_mask is not None:attn_mask = attn_mask.unsqueeze(1) # [batch_size, 1, seq_len, seq_len] 廣播到所有頭# 應用ScaledDotProductAttentioncontext, attn_weights = self.attention(q_s, k_s, v_s, attn_mask=attn_mask)# 將各頭的輸出拼接起來# (batch_size, n_heads, seq_len, d_v) -> (batch_size, seq_len, n_heads * d_v) = (batch_size, seq_len, d_model)context = context.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model)# 輸出投影output = self.W_O(context)output = self.dropout(output)# 殘差連接和層歸一化output = self.layer_norm(output + residual)return output, attn_weights

3. Position-wise Feed-Forward Network

這是一個簡單的前饋神經網絡,對每個位置(詞)的特征進行獨立變換。它通常包含兩個線性層和一個ReLU激活函數。

class PositionWiseFFN(nn.Module):"""Position-wise Feed-Forward Network"""def __init__(self, d_model, d_ff, dropout_rate=0.1):super(PositionWiseFFN, self).__init__()self.w_1 = nn.Linear(d_model, d_ff)self.w_2 = nn.Linear(d_ff, d_model)self.dropout = nn.Dropout(dropout_rate)self.layer_norm = nn.LayerNorm(d_model)def forward(self, x):residual = xx = self.w_1(x)x = F.relu(x)x = self.dropout(x)x = self.w_2(x)x = self.dropout(x)# 殘差連接和層歸一化x = self.layer_norm(x + residual)return x

4. Positional Encoding(位置編碼)

由于Transformer沒有循環和卷積結構,它無法感知序列的順序。因此,我們需要手動注入位置信息。這里我們使用論文中的正弦和余弦函數編碼。

class PositionalEncoding(nn.Module):"""Implement the PE function."""def __init__(self, d_model, max_seq_len=5000):super(PositionalEncoding, self).__init__()# 創建一個足夠長的位置編碼矩陣pe = torch.zeros(max_seq_len, d_model)position = torch.arange(0, max_seq_len, dtype=torch.float).unsqueeze(1) # [max_seq_len, 1]div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))# 對矩陣的偶數和奇數索引分別用正弦和余弦函數pe[:, 0::2] = torch.sin(position * div_term)pe[:, 1::2] = torch.cos(position * div_term)pe = pe.unsqueeze(0) # [1, max_seq_len, d_model]# 注冊為一個緩沖區(buffer),它將是模型的一部分,但不被視為可訓練參數self.register_buffer('pe', pe)def forward(self, x):# x 的形狀: [batch_size, seq_len, d_model]x = x + self.pe[:, :x.size(1), :]return x

第三部分:組裝編碼器與解碼器層

有了上面的積木,我們現在可以搭建編碼器層和解碼器層了。

1. 編碼器層(Encoder Layer)

一個編碼器層包含一個多頭自注意力子層和一個前饋網絡子層。

class EncoderLayer(nn.Module):"""A single layer of the encoder."""def __init__(self, d_model, n_heads, d_ff, dropout_rate=0.1):super(EncoderLayer, self).__init__()self.self_attn = MultiHeadAttention(d_model, n_heads, dropout_rate)self.ffn = PositionWiseFFN(d_model, d_ff, dropout_rate)def forward(self, enc_input, enc_self_attn_mask=None):# 自注意力子層enc_output, attn_weights = self.self_attn(enc_input, enc_input, enc_input, attn_mask=enc_self_attn_mask)# 前饋網絡子層enc_output = self.ffn(enc_output)return enc_output, attn_weights

2. 解碼器層(Decoder Layer)

一個解碼器層包含三個子層:掩碼自注意力、編碼器-解碼器注意力和前饋網絡。

class DecoderLayer(nn.Module):"""A single layer of the decoder."""def __init__(self, d_model, n_heads, d_ff, dropout_rate=0.1):super(DecoderLayer, self).__init__()self.self_attn = MultiHeadAttention(d_model, n_heads, dropout_rate)self.enc_dec_attn = MultiHeadAttention(d_model, n_heads, dropout_rate)self.ffn = PositionWiseFFN(d_model, d_ff, dropout_rate)def forward(self, dec_input, enc_output, dec_self_attn_mask=None, dec_enc_attn_mask=None):# 掩碼自注意力子層dec_output, self_attn_weights = self.self_attn(dec_input, dec_input, dec_input, attn_mask=dec_self_attn_mask)# 編碼器-解碼器注意力子層# Q 來自解碼器,K, V 來自編碼器輸出dec_output, enc_dec_attn_weights = self.enc_dec_attn(dec_output, enc_output, enc_output, attn_mask=dec_enc_attn_mask)# 前饋網絡子層dec_output = self.ffn(dec_output)return dec_output, self_attn_weights, enc_dec_attn_weights

第四部分:構建完整的Transformer模型

現在,我們將嵌入層、位置編碼、編碼器棧、解碼器棧以及最終的輸出層組合在一起。

class Transformer(nn.Module):"""The complete Transformer model."""def __init__(self, src_vocab_size, tgt_vocab_size, d_model, n_heads, n_layers, d_ff, max_seq_len, dropout_rate=0.1):super(Transformer, self).__init__()self.d_model = d_model# 輸入和輸出嵌入層,共享權重通常效果更好,但這里我們先分開self.enc_embedding = nn.Embedding(src_vocab_size, d_model)self.dec_embedding = nn.Embedding(tgt_vocab_size, d_model)self.pos_encoding = PositionalEncoding(d_model, max_seq_len)# 編碼器和解碼器堆疊self.encoder_layers = nn.ModuleList([EncoderLayer(d_model, n_heads, d_ff, dropout_rate) for _ in range(n_layers)])self.decoder_layers = nn.ModuleList([DecoderLayer(d_model, n_heads, d_ff, dropout_rate) for _ in range(n_layers)])# 最終的線性層和softmaxself.linear = nn.Linear(d_model, tgt_vocab_size)self.dropout = nn.Dropout(dropout_rate)def forward(self, src_input, tgt_input, src_mask=None, tgt_mask=None):# 編碼器部分enc_output = self.enc_embedding(src_input) * math.sqrt(self.d_model)enc_output = self.pos_encoding(enc_output)enc_output = self.dropout(enc_output)for layer in self.encoder_layers:enc_output, _ = layer(enc_output, enc_self_attn_mask=src_mask)# 解碼器部分dec_output = self.dec_embedding(tgt_input) * math.sqrt(self.d_model)dec_output = self.pos_encoding(dec_output)dec_output = self.dropout(dec_output)for layer in self.decoder_layers:dec_output, _, _ = layer(dec_output, enc_output, dec_self_attn_mask=tgt_mask)# 輸出投影output = self.linear(dec_output)# Softmax在損失函數中計算,這里直接返回logitsreturn output

第五部分:數據準備與訓練

1. 選擇一個迷你數據集

為了快速實驗,我們使用一個非常小的對話數據集。例如,我們可以手動創建一個:

Q: Hi
A: Hello!
Q: What's your name?
A: I'm ChatBot.
Q: How are you?
A: I'm fine, thank you.
... (再多幾十組)

或者使用Cornell Movie Dialogs Corpus的一小部分。我們需要構建一個詞匯表,并將句子轉換為ID序列。

2. 構建詞匯表和DataLoader

# 偽代碼:構建詞匯表
# sentences = [所有Q和A的句子]
# vocab = {'<pad>':0, '<sos>':1, '<eos>':2, ...} 構建詞匯字典
# src_ids = [[vocab[word] for word in sentence.split()] for sentence in src_sentences]# 使用PyTorch的DataLoader和Dataset
from torch.utils.data import Dataset, DataLoaderclass ChatDataset(Dataset):def __init__(self, src_sentences, tgt_sentences, vocab, max_len):self.src_sentences = src_sentencesself.tgt_sentences = tgt_sentencesself.vocab = vocabself.max_len = max_lendef __len__(self):return len(self.src_sentences)def __getitem__(self, idx):src_seq = self.sentence_to_ids(self.src_sentences[idx])tgt_seq = self.sentence_to_ids(self.tgt_sentences[idx])# 添加起始符<sos>和結束符<eos>tgt_input = [self.vocab['<sos>']] + tgt_seqtgt_output = tgt_seq + [self.vocab['<eos>']]# 填充到最大長度src_seq = self.pad_seq(src_seq, self.max_len)tgt_input = self.pad_seq(tgt_input, self.max_len)tgt_output = self.pad_seq(tgt_output, self.max_len)return torch.LongTensor(src_seq), torch.LongTensor(tgt_input), torch.LongTensor(tgt_output)# ... (實現sentence_to_ids和pad_seq方法)

3. 創建注意力掩碼和訓練循環

我們需要創建兩種掩碼:

  1. 填充掩碼(Padding Mask):遮蓋掉<pad>符號,防止注意力機制關注這些無意義的位置。
  2. 序列掩碼(Sequence Mask):用于解碼器的自注意力,防止解碼時看到未來的信息(一個下三角矩陣)。
def create_padding_mask(seq, pad_idx):# seq: [batch_size, seq_len]return (seq == pad_idx).unsqueeze(1).unsqueeze(2) # [batch_size, 1, 1, seq_len] 便于廣播def create_look_ahead_mask(seq_len):# 創建一個下三角矩陣,對角線及其以上為0,以下為1mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1).bool()return mask.unsqueeze(0).unsqueeze(0) # [1, 1, seq_len, seq_len]

訓練循環 的標準流程:準備數據、計算模型輸出、計算損失(帶忽略<pad>的CrossEntropyLoss)、反向傳播、優化器步進。

# 初始化模型、優化器、損失函數
model = Transformer(src_vocab_size, tgt_vocab_size, d_model=512, n_heads=8, n_layers=6, d_ff=2048, max_seq_len=100)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4, betas=(0.9, 0.98), eps=1e-9)
criterion = nn.CrossEntropyLoss(ignore_index=pad_idx) # 忽略padding位置的損失for epoch in range(num_epochs):model.train()for batch in dataloader:src, tgt_in, tgt_out = batchsrc_mask = create_padding_mask(src, pad_idx)# 解碼器掩碼:填充掩碼 + 序列掩碼tgt_padding_mask = create_padding_mask(tgt_in, pad_idx)tgt_look_ahead_mask = create_look_ahead_mask(tgt_in.size(1))tgt_mask = torch.logical_or(tgt_padding_mask, tgt_look_ahead_mask) # 組合掩碼optimizer.zero_grad()output = model(src, tgt_in, src_mask, tgt_mask)# output: [batch_size, tgt_len, tgt_vocab_size]# tgt_out: [batch_size, tgt_len]loss = criterion(output.view(-1, output.size(-1)), tgt_out.view(-1))loss.backward()optimizer.step()# ... 每個epoch結束后可以打印損失或進行驗證

第六部分:推理與對話生成

訓練完成后,我們使用貪心搜索(Greedy Search)來生成回復。

def predict(model, src_sentence, vocab, inv_vocab, max_len, device):model.eval()with torch.no_grad():# 將源句子轉換為IDsrc_ids = sentence_to_ids(src_sentence, vocab)src_tensor = torch.LongTensor(src_ids).unsqueeze(0).to(device) # [1, src_len]# 初始化目標輸入,起始為<sos>tgt_ids = [vocab['<sos>']]for i in range(max_len):tgt_tensor = torch.LongTensor(tgt_ids).unsqueeze(0).to(device) # [1, current_tgt_len]# 創建掩碼src_mask = create_padding_mask(src_tensor, pad_idx)tgt_mask = create_look_ahead_mask(len(tgt_ids))# 預測下一個詞output = model(src_tensor, tgt_tensor, src_mask, tgt_mask)next_word_logits = output[0, -1, :] # 最后一個位置的輸出next_word_id = torch.argmax(next_word_logits, dim=-1).item()tgt_ids.append(next_word_id)if next_word_id == vocab['<eos>']:break# 將ID序列轉換回句子,忽略<sos>和<eos>predicted_sentence = ids_to_sentence(tgt_ids[1:-1], inv_vocab)return predicted_sentence# 示例用法
# vocab: 詞匯表,inv_vocab: 反向詞匯表(id到word)
# response = predict(model, "Hello there", vocab, inv_vocab, max_len=20, device='cpu')
# print(response)

總結與展望

恭喜你!你已經從零開始實現并訓練了一個Transformer模型。這個過程無疑充滿挑戰,但它極大地深化了你對Self-Attention、位置編碼、掩碼等核心概念的理解。

我們的迷你ChatBot雖然簡單,但它已經具備了Transformer架構的所有精髓。你可以通過以下方式進一步提升它:

  1. 使用更大更高質量的數據集
  2. 調整超參數d_model, n_heads, n_layers, d_ff等。
  3. 實現更高級的解碼策略:如Beam Search。
  4. 嘗試預訓練:在海量文本上先進行無監督預訓練,再在我們的對話數據上進行微調。
  5. 加入更多技巧:如標簽平滑、學習率預熱等。

希望這次“手撕”Transformer的經歷讓你不再覺得它神秘莫測。它只是一個精心設計的神經網絡,其力量源于對序列數據中復雜依賴關系的強大建模能力。現在,你已經掌握了它的藍圖,可以自由地去探索、修改和創新了!

(注意:由于篇幅和可運行性限制,本文代碼為示例性質,可能需要一些調試和修改才能完全運行。建議在Jupyter Notebook或Colab中分模塊逐步測試。)


點擊AladdinEdu,同學們用得起的【H卡】算力平臺”,注冊即送-H卡級別算力80G大顯存按量計費靈活彈性頂級配置學生更享專屬優惠

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/pingmian/97832.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/97832.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/97832.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【20期】滬深指數《實時交易數據》免費獲取股票數據API:PythonJava等5種語言調用實例演示與接口API文檔說明

? 隨著量化投資在金融市場的快速發展&#xff0c;高質量數據源已成為量化研究的核心基礎設施。本文將系統介紹股票量化分析中的數據獲取解決方案&#xff0c;涵蓋實時行情、歷史數據及基本面信息等關鍵數據類型。 本文將重點演示這些接口在以下技術棧中的實現&#xff1a; P…

RabbitMQ如何保障消息的可靠性

文章目錄什么是消息可靠性&#xff1f;RabbitMQ消息可靠性的三個維度1. 生產者到Exchange的可靠性2. Exchange到Queue的可靠性3. Queue到消費者的可靠性核心機制詳解Publisher Confirm機制消息持久化Mandatory參數消費者確認機制&#xff08;ACK&#xff09;最佳實踐建議1. 合理…

二十、DevOps落地:Jenkins基礎入門(一)

二十、DevOps落地&#xff1a;Jenkins基礎入門&#xff08;一&#xff09; 文章目錄二十、DevOps落地&#xff1a;Jenkins基礎入門&#xff08;一&#xff09;1、DevOps初識1.1 什么是DevOps1.2 DevOps相關工具鏈1.3 什么是CICD&#xff1f;1.4 持續集成CI介紹1.5 持續交付和持…

簡單易實現的數據校驗方法Checksum

簡單易實現的數據校驗方法Checksum 在數據傳輸中&#xff0c;Checksum&#xff08;校驗和&#xff09; 扮演著 “數據完整性哨兵” 的角色。它的主要作用是 快速檢測數據在傳輸過程中是否發生了錯誤 。 下面我將詳細解釋它的作用、工作原理、優缺點以及典型應用。 核心作用&…

再次深入學習深度學習|花書筆記1

我已經兩年沒有碰過深度學習了&#xff0c;寫此文記錄學習過程&#xff0c;加深理解。 深度學習再次深入學習深度學習|花書筆記1信息論第四節 數值計算中的問題上溢出 和 下溢出病態條件優化法再次深入學習深度學習|花書筆記1 這本書說的太繁瑣了&#xff0c;如果是想要基于這…

DeerFlow實踐:華為LTC流程的評審智能體設計

目錄 一、機制設計核心邏輯 二、4 個評審點智能體機制詳解 &#xff08;一&#xff09;立項決策&#xff08;ATI&#xff09;智能體機制 1. 知識調用與匹配 2. 評審校驗流程 3. 異常處理 &#xff08;二&#xff09;投標決策&#xff08;ATB&#xff09;智能體機制 1. …

C++與Lua交互:從原理到實踐指南

核心原理&#xff1a;Lua虛擬棧機制 C與Lua能夠高效交互的核心在于Lua虛擬棧的設計&#xff0c;這是一個精巧的中立通信區&#xff0c;解決了兩種語言間的本質差異&#xff1a;特性對比CLua語言類型靜態編譯型動態解釋型數據管理明確內存布局虛擬機統一管理類型系統編譯時確定運…

CSS 編碼規范

CSS 編碼規范1 CSS1.1 編碼規范1.1.1 【強制】所有聲明必須以分號結尾1.1.2 【推薦】使用 2 個空格縮進1.1.3 【推薦】選擇器與 { 之間保留一個空格1.1.4 【推薦】屬性值規范1.1.5 【推薦】組合器規范1.1.6 【推薦】逗號分隔規范1.1.7 【推薦】注釋規范1.1.8 【推薦】右大括號規…

ORA-12514:TNS:監聽程序當前無法識別連接描述符中請求的服務

已經不止一次自己本機電腦安裝的Oracle使用plsqldev軟件登入提示這個了.一般前一天還好好的&#xff0c;今天就不行了.好好總結一下吧&#xff0c;也共大家一起借鑒.主要原因還是數據的歸檔日志因為內部內存已經耗盡&#xff0c;不能在進行歸檔導致數據庫啟動異常&#xff0c;沒…

Spring框架的JDBC模板技術和事務管理

SpringJDBCJDBC模板技術概述JDBC的模板類的使用Spring框架的事務管理配置文件方式半注解的方式純注解的方式JDBC模板技術概述 什么是 JDBC 模板技術&#xff1f; JDBC 模板技術是 Spring 框架為簡化持久層&#xff08;數據庫操作&#xff09;編程而提供的一種封裝機制&#xf…

將文件部署到受管主機

目錄 1.ansible.builtin中用于創建、更新或刪除多行文本塊的模塊是什么 2.copy模塊的作用 3.fetch模塊的作用 4.file模塊的作用 5.lineinfile模塊的作用 6.stat模塊的作用 7.要確保受管主機上存在文件&#xff0c;類似touch命令功能&#xff0c;還能設置權限等的模塊及操作是怎…

Dell PowerEdge R620 服務器內存和硬盤罷工了

文章目錄前言調查原因查找解決方案硬盤問題內存問題總結前言 月黑風高夜&#xff0c;服務宕機時。做服務端技術的&#xff0c;誰還沒半夜遇到個服務掛掉的情況&#xff0c;而像我這種半兼職網管的工作&#xff0c;遇到機器問題的概率也就更大了&#xff0c;本來周五晚上寫完總…

2025:SourceTree 啟用/禁用Mercurial 或 Git,像素級細節

最近使用Git管理工具的時候&#xff0c;發現還是SourceTree好用些&#xff0c;但是使用SourceTree帶來一個問題&#xff1a;就是每次在重新打開SourceTree的時候&#xff0c;都會重新下載Mercurial.zip文件&#xff0c;查了一下&#xff0c;一般情況下我們是不需要使用Mercuria…

安卓 Google Maps 的使用和開發步驟

文章目錄1. main2. Android 谷歌地圖3. 源碼Reference1. main 在國內選擇的SDK可以是高德、百度、騰訊、xxxx等&#xff0c;但在國外&#xff0c;你首選是谷歌&#xff0c;因此要進行Google地圖的開發你首先要解決下面三個問題 VPN Google賬號 信用卡American Express&#x…

Linux -- 應用層協議Http

1.HTTP背景知識 HTTP協議&#xff1a;HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本傳輸協議&#xff09;的本質是運行在 TCP/IP 協議族之上的 “應用層協議”&#xff0c;核心作用是定義客戶端&#xff08;如瀏覽器、APP&#xff09;與服務器之間的 “數據…

R 語言本身并不直接支持 Python 中 f“{series_matrix}.txt“ 這樣的字符串字面量格式化(f-string)語法 glue函數

R 語言本身并不直接支持 Python 中 f"{series_matrix}.txt" 這樣的字符串字面量格式化&#xff08;f-string&#xff09;語法。 在 R 中&#xff0c;要實現字符串拼接或格式化&#xff0c;你需要使用其他方法。下表對比了 Python f-string 和 R 中常見對應方法的主要…

【AI智能體】亮數據MCP Server × Dify:AI智能體獲取實時影音數據就是這么簡單

文章目錄一、引言&#xff1a;AI 應用與實時影音數據的融合價值1、傳統采集方式的痛點2、MCP Server 的創新價值二、亮數據 MCP Server 概覽1、什么是 MCP Server&#xff1f;2、支持的影音平臺和API接口3、產品特色亮點三、業務場景示例設計1、選定場景&#xff1a;競品分析與…

從《Attention Is All You Need》深入理解Transformer

2017年的《Attention Is All You Need》論文提出的Transformer架構&#xff0c;不僅徹底改變了自然語言處理的格局&#xff0c;更為現代人工智能的發展奠定了堅實基礎。本文將帶你深入解析這一劃時代模型的核心思想、技術細節及其深遠影響。&#x1f504; 一、背景與動機&#…

【08】AI輔助編程完整的安卓二次商業實戰-修改消息聊天框背景色-觸發聊天讓程序異常終止bug牽涉更多聊天消息發送優化處理-優雅草卓伊凡

【08】AI輔助編程完整的安卓二次商業實戰-修改消息聊天框背景色-觸發聊天讓程序異常終止bug牽涉更多聊天消息發送優化處理-優雅草卓伊凡引言本次二開布局沒有變&#xff0c;但是下一次整體布局會有變&#xff0c;不過本次開發發現朋友圈跳轉功能的流程步驟也做了一定的變化。原…

心理調適與情緒管理實訓室:支撐康養旅游人才心理能力培養

在康養休閑旅游服務專業的教學體系中&#xff0c;心理調適與情緒管理實訓室作為關鍵教學場所&#xff0c;承擔著培養學生心理服務能力、情緒疏導技能和人際溝通素養的重要任務。隨著社會對康養旅游服務質量要求的提升&#xff0c;具備心理調適與情緒管理能力的專業人才日益受到…