基于Transformer的機器翻譯——訓練篇

前言

還在為機器翻譯模型從理論到落地卡殼?系列博客第三彈——模型訓練篇強勢登場,手把手帶你走完Transformer中日翻譯項目的最后關鍵一步!

前兩期我們搞定了數據預處理(分詞、詞表構建全流程)和模型搭建(詞嵌入、位置編碼、編碼器解碼器核心結構),而這一篇,將聚焦讓模型“學會翻譯”的核心秘籍:

  • 如何設計損失函數,讓模型精準捕捉中日語言差異?
  • 優化器參數怎么調,才能讓訓練更穩定、收斂更快?

從數據到模型,再到訓練全流程,一套代碼跑通Transformer翻譯任務。

本篇主要介紹機器翻譯項目中模型訓練部分是如何處理的,延續之前篇章預處理篇,模型篇的內容。
系列博客第一彈——基于Transformer的機器翻譯——預處理篇
系列博客第二彈——基于Transformer的機器翻譯——模型篇
系列博客第三彈——基于Transformer的機器翻譯——訓練篇

1.模型介紹

1.1模型參數配置

參數主要有源語言與目標語言詞表大小詞嵌入維度多頭注意力頭數前饋神經網絡隱藏層維度編碼器與解碼器層數批量大小等,這些均屬于超參數,需要在訓練中不斷調整,考慮到自身硬件條件,超參數設置如下:

# 模型參數配置
SRC_VOCAB_SIZE = len(ja_vocab)  # 源語言(日語)詞匯表大小
TGT_VOCAB_SIZE = len(ch_vocab)  # 目標語言(中文)詞匯表大小
EMB_SIZE = 512                  # 詞嵌入維度(與Transformer的d_model一致)
NHEAD = 8                       # 多頭注意力的頭數
FFN_HID_DIM = 512               # 前饋網絡隱藏層維度
BATCH_SIZE = 16                 # 批量大小(每次輸入的樣本數)
NUM_ENCODER_LAYERS = 3          # 編碼器層數
NUM_DECODER_LAYERS = 3          # 解碼器層數
NUM_EPOCHS = 16                 # 訓練輪數(完整遍歷數據集的次數)

接著初始化網絡模型:

transformer = Seq2SeqTransformer(NUM_ENCODER_LAYERS,NUM_DECODER_LAYERS,EMB_SIZE,SRC_VOCAB_SIZE,TGT_VOCAB_SIZE,FFN_HID_DIM
)

1.2模型結構

可以直接打印模型,查看模型結構,如下:

transformer

輸出結果:

Seq2SeqTransformer((transformer_encoder): TransformerEncoder((layers): ModuleList((0-2): 3 x TransformerEncoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=512, out_features=512, bias=True))(linear1): Linear(in_features=512, out_features=512, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=512, out_features=512, bias=True)(norm1): LayerNorm((512,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((512,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False))))(transformer_decoder): TransformerDecoder((layers): ModuleList((0-2): 3 x TransformerDecoderLayer((self_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=512, out_features=512, bias=True))(multihead_attn): MultiheadAttention((out_proj): NonDynamicallyQuantizableLinear(in_features=512, out_features=512, bias=True))(linear1): Linear(in_features=512, out_features=512, bias=True)(dropout): Dropout(p=0.1, inplace=False)(linear2): Linear(in_features=512, out_features=512, bias=True)(norm1): LayerNorm((512,), eps=1e-05, elementwise_affine=True)(norm2): LayerNorm((512,), eps=1e-05, elementwise_affine=True)(norm3): LayerNorm((512,), eps=1e-05, elementwise_affine=True)(dropout1): Dropout(p=0.1, inplace=False)(dropout2): Dropout(p=0.1, inplace=False)(dropout3): Dropout(p=0.1, inplace=False))))(generator): Linear(in_features=512, out_features=26854, bias=True)(src_tok_emb): TokenEmbedding((embedding): Embedding(24058, 512))(tgt_tok_emb): TokenEmbedding((embedding): Embedding(26854, 512))(positional_encoding): PositionalEncoding((dropout): Dropout(p=0.1, inplace=False))
)

1.3參數初始化

一般主要有兩種初始化模型參數的方法,包括He初始化(即一般默認的初始化方法)Xavier初始化方法

  • He初始化:適用于使用ReLU或其變種Leaky ReLU這類激活函數的神經網絡。
  • Xavier初始化:適用于tanhsigmoid等這類對稱激活函數。

這里采用Xavier初始化,代碼如下:

# 參數初始化(Xavier均勻初始化,緩解梯度消失/爆炸)
for p in transformer.parameters():if p.dim() > 1:  # 僅初始化非標量參數nn.init.xavier_uniform_(p)

為了加快模型的訓練速度,這里使用GPU加速,即將模型遷移至GPU上進行訓練。

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
transformer = transformer.to(device)

如果未配置GPU環境,此處會默認使用CPU。

2.訓練策略

2.1損失函數

因為該問題最終是一個分類問題,預測詞表中的一個單詞。而分類問題,一般采用交叉熵損失函數。同時,因為此前我們填充<PAD_IDX>字符使輸入句子長度保持一致,所以在計算交叉熵的時候,應該將該字符產生的損失忽略。
代碼如下:

loss_fn = torch.nn.CrossEntropyLoss(ignore_index=PAD_IDX)

2.2優化器

優化器大都會選用自適應優化器Adam,相對于常規的SGD(隨機梯度下降方法),避免了頻繁調整參數,同時能夠更快的收斂。代碼如下:

optimizer = torch.optim.Adam(transformer.parameters(),  # 待優化的模型參數lr=0.0001,                  # 學習率(控制參數更新步長)betas=(0.9, 0.98),          # 動量參數(控制歷史梯度的衰減)eps=1e-9                    # 數值穩定性參數(防止除零)
)

2.3訓練過程

整體的訓練過程主要包括五個步驟:

  1. 梯度清零
  2. 計算模型輸出
  3. 計算損失
  4. 方向傳播
  5. 參數更新

模型輸入包括兩部分,輸入的源語言,和目標語言已經生成的部分,分別對應于編碼器的輸入和解碼器的輸入。
因為是逐個token進行預測,所以編碼器的輸入不包括句子的最后一個tokenEOS_IDX,模型預測輸出到EOS_IDX即結束,相應的監督標簽為目標語言不包括第一個token即BOS_IDX
相應代碼為:

src = src.to(device)       # 將源序列移動到GPU/CPUtgt = tgt.to(device)       # 將目標序列移動到GPU/CPUtgt_input = tgt[:-1, :]    # 目標輸入去掉最后一個詞tgt_output = tgt[1:, :]   # 目標輸出去掉第一個詞

因為模型在進行訓練時,不能關注到目標輸入后續的token,因此需要掩碼注意力機制

2.3.1順序掩碼

采用下三角掩碼,防止模型在預測第i個詞時關注到第i+1、i+2… 個詞(即 “未來信息”)

  1. 通過torch.triu(torch.ones((3,3))) 生成上三角矩陣(對角線及以上為 1,其余為 0)
  2. ==1 轉為布爾矩陣(True 表示原位置為 1),再 transpose(0,1) 轉置(行變列,列變行)
  3. masked_fill 填充:True 位置(有效)填 0.0,False 位置(無效,未來信息)填-inf
    本案例主要通過函數generate_square_subsequent_mask函數實現,代碼如下:
def generate_square_subsequent_mask(sz):# 1. 生成上三角矩陣(對角線及以上為1,其余為0)mask = (torch.triu(torch.ones((sz, sz), device=device)) == 1).transpose(0, 1)# 2. 將0的位置填充為-inf(無效位置),1的位置填充為0(有效位置)mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))return mask

2.3.2完整掩碼組合

這里主要4 種掩碼,分別用于源序列和目標序列的注意力計算。

2.3.2.1源序列自注意力掩碼

源序列是輸入,無需掩蓋 “未來信息”,因此全為 0(允許關注所有位置)。

2.3.2.2目標序列自注意力掩碼

可以通過調用 generate_square_subsequent_mask實現。

2.3.2.3源序列填充掩碼

標記源序列中PAD的位置,讓模型在注意力計算時忽略這些無效位置。

2.3.2.4目標序列填充掩碼

類似源序列,標記目標序列中PAD的位置。

2.3.2.5完整代碼

上述四種掩碼的生成代碼可表述為以下代碼:

def create_mask(src, tgt):src_seq_len = src.shape[0]  # 源序列長度(seq_len)tgt_seq_len = tgt.shape[0]  # 目標序列長度(seq_len)# 目標序列的注意力掩碼(下三角掩碼,防止關注未來詞)tgt_mask = generate_square_subsequent_mask(tgt_seq_len)# 源序列的注意力掩碼(全0,允許關注所有位置)src_mask = torch.zeros((src_seq_len, src_seq_len), device=device).type(torch.bool)# 源序列的填充掩碼(標記<pad>的位置)src_padding_mask = (src == PAD_IDX).transpose(0, 1)# 目標序列的填充掩碼(標記<pad>的位置)tgt_padding_mask = (tgt == PAD_IDX).transpose(0, 1)return src_mask, tgt_mask, src_padding_mask, tgt_padding_mask

2.3.3訓練代碼

由上述代碼可得,訓練過程可表述為以下代碼:

  1. 獲取當前批量的輸入與目標輸出序列
  2. 生成掩碼
  3. 計算模型預測輸出
  4. 清空梯度
  5. 計算預測輸出與目標輸出之間的損失
  6. 方向傳播
  7. 更新梯度
for idx, (src, tgt) in enumerate(train_iter):  # 遍歷訓練數據迭代器src = src.to(device)       # 將源序列移動到GPU/CPUtgt = tgt.to(device)       # 將目標序列移動到GPU/CPUtgt_input = tgt[:-1, :]    # 目標輸入去掉最后一個詞tgt_output = tgt[1:, :]   # 目標輸出去掉第一個詞# 生成掩碼(注意力掩碼+填充掩碼)src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_mask(src, tgt_input)# 前向傳播:模型預測logits = model(src, tgt_input, src_mask, tgt_mask,src_padding_mask, tgt_padding_mask, src_padding_mask)optimizer.zero_grad()      # 清空優化器梯度tgt_out = tgt[1:, :]       # 目標輸出(去掉第一個詞,與預測對齊)loss = loss_fn(            # 計算損失logits.reshape(-1, logits.shape[-1]),  # 展平預測結果([seq_len*batch_size, vocab_size])tgt_out.reshape(-1)                    # 展平真實標簽([seq_len*batch_size]))loss.backward()            # 反向傳播計算梯度optimizer.step()           # 更新模型參數losses += loss.item()      # 累加單批損失

為了減小梯度抖動,采用當前iter內所有損失的平均和,同時為了可視化損失折線圖,加入了tensorboard可視化,并加入進度條來顯示訓練過程,當前訓練過程。代碼如下:

def train_epoch(model, train_iter, optimizer):model.train()  # 開啟訓練模式(啟用Dropout等)losses = 0     # 累計損失值for idx, (src, tgt) in enumerate(train_iter):  # 遍歷訓練數據迭代器src = src.to(device)       # 將源序列移動到GPU/CPUtgt = tgt.to(device)       # 將目標序列移動到GPU/CPUtgt_input = tgt[:-1, :]    # 目標輸入去掉最后一個詞tgt_output = tgt[1:, :]   # 目標輸出去掉第一個詞# 生成掩碼(注意力掩碼+填充掩碼)src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_mask(src, tgt_input)# 前向傳播:模型預測logits = model(src, tgt_input, src_mask, tgt_mask,src_padding_mask, tgt_padding_mask, src_padding_mask)optimizer.zero_grad()      # 清空優化器梯度tgt_out = tgt[1:, :]       # 目標輸出(去掉第一個詞,與預測對齊)loss = loss_fn(            # 計算損失logits.reshape(-1, logits.shape[-1]),  # 展平預測結果([seq_len*batch_size, vocab_size])tgt_out.reshape(-1)                    # 展平真實標簽([seq_len*batch_size]))# 寫入tensorboardwriter.add_scalar('train_loss',loss.item(),idx)loss.backward()            # 反向傳播計算梯度optimizer.step()           # 更新模型參數losses += loss.item()      # 累加單批損失train_bar.set_postfix({'loss': '{:.6f}'.format(loss.item())})train_bar.update()return losses / len(train_iter)  # 返回平均訓練損失

為了避免長時間等待,因此使用當前數據集的10%,其中9%作為訓練集,1%作為測試集。代碼如下:

train_iter = DataLoader(train_data[:int(len(train_data)*0.09)], batch_size=BATCH_SIZE,shuffle=True, collate_fn=generate_batch)
test_iter = DataLoader(train_data[int(len(train_data)*0.09):int(len(train_data)*0.1)], batch_size=BATCH_SIZE,shuffle=True, collate_fn=generate_batch)

3.訓練結果

假如此時按照每個iter來進行記錄損失(上述代碼就是如此),結果會出現劇烈波動,整體還是會保持下降趨勢,結果如下:
在這里插入圖片描述
此時將其改成每個epoch來記錄損失,取當前epoch的平均值,結果如下:
在這里插入圖片描述
此時只統計了12個點,當然你可以通過增加epoch輪數來增加統計的點數,但是曲線的變化趨勢往往是判斷模型過擬合和欠擬合的關鍵,因此一般采用每多個iter內的加權值來統計一次損失。這里為了方便起見,采用的仍是按照epoch進行統計的。

同時為了可視化訓練進度,此處加入了進度條來實時展示訓練的進度。統計損失使用tensorboard進行可視化。
完整訓練過程代碼為:
訓練函數:

writer=SummaryWriter('./logs')
def train_epoch(model, train_iter, optimizer,epoch):model.train()  # 開啟訓練模式(啟用Dropout等)losses = 0     # 累計損失值for idx, (src, tgt) in enumerate(train_iter):  # 遍歷訓練數據迭代器src = src.to(device)       # 將源序列移動到GPU/CPUtgt = tgt.to(device)       # 將目標序列移動到GPU/CPUtgt_input = tgt[:-1, :]    # 目標輸入去掉最后一個詞tgt_output = tgt[1:, :]   # 目標輸出去掉第一個詞# 生成掩碼(注意力掩碼+填充掩碼)src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_mask(src, tgt_input)# 前向傳播:模型預測logits = model(src, tgt_input, src_mask, tgt_mask,src_padding_mask, tgt_padding_mask, src_padding_mask)optimizer.zero_grad()      # 清空優化器梯度tgt_out = tgt[1:, :]       # 目標輸出(去掉第一個詞,與預測對齊)loss = loss_fn(            # 計算損失logits.reshape(-1, logits.shape[-1]),  # 展平預測結果([seq_len*batch_size, vocab_size])tgt_out.reshape(-1)                    # 展平真實標簽([seq_len*batch_size]))loss.backward()            # 反向傳播計算梯度optimizer.step()           # 更新模型參數losses += loss.item()      # 累加單批損失train_bar.set_postfix({'loss': '{:.6f}'.format(loss.item())})train_bar.update()# 寫入tensorboardwriter.add_scalar('train_loss',losses / len(train_iter),epoch)return losses / len(train_iter)  # 返回平均訓練損失

驗證函數:

def evaluate(model, test_iter,epoch):model.eval()losses = 0for src, tgt in test_iter:src = src.to(device)tgt = tgt.to(device)tgt_input = tgt[:-1, :]tgt_output = tgt[1:, :]src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_mask(src, tgt_input)logits = model(src, tgt_input, src_mask, tgt_mask, src_padding_mask, tgt_padding_mask, src_padding_mask)tgt_out = tgt[1:, :]loss = loss_fn(logits.reshape(-1, logits.shape[-1]), tgt_out.reshape(-1))losses += loss.item()test_bar.set_postfix({'loss': '{:.6f}'.format(loss.item())})test_bar.update()writer.add_scalar('test_loss',losses / len(test_iter),epoch)return losses / len(test_iter)

調用函數:

from tqdm.notebook import tqdm
epoch_bar = tqdm(desc='training routine',total=NUM_EPOCHS,position=0)train_bar = tqdm(desc='split=train',total=len(train_iter),position=0,leave=True)
test_bar=tqdm(desc='split=test',total=len(test_iter),position=0,leave=True)
best_loss = float('inf')
for epoch in range(1, NUM_EPOCHS+1):start_time = time.time()train_loss = train_epoch(transformer, train_iter, optimizer,epoch)test_loss = evaluate(transformer, test_iter,epoch)if test_loss < best_loss:best_loss = test_loss# 保存時分開保存torch.save(transformer.state_dict(), './share/model_weights.pth')  # 只保存權重torch.save({'ja_vocab': ja_vocab, 'ch_vocab': ch_vocab}, './share/vocab.pth')  # 保存詞表print(f"Epoch: {epoch}, Train loss: {train_loss:.3f}, Model saved.")epoch_bar.set_postfix({'train_loss': '{:.6f}'.format(train_loss)})epoch_bar.update()train_bar.n = 0test_bar.n=0end_time = time.time()print((f"Epoch: {epoch}, Train loss: {train_loss:.3f}, "f"Epoch time = {(end_time - start_time):.3f}s"))

訓練結果:
在這里插入圖片描述
在這里插入圖片描述

結語

至此,基于Transformer的機器翻譯任務就介紹完畢了,至于模型性能的評價指標如BLEU并未做介紹,感興趣的可以自行探索,希望本案例,能夠對你有所幫助,感謝支持!
系列博客第一彈——基于Transformer的機器翻譯——預處理篇
系列博客第二彈——基于Transformer的機器翻譯——模型篇
系列博客第三彈——基于Transformer的機器翻譯——訓練篇

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

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

相關文章

智能編程中的智能體與 AI 應用:概念、架構與實踐場景

一、智能體&#xff08;Intelligent Agent&#xff09;在編程中的定義與架構1. 智能體的核心概念 智能體是指在特定環境中能夠自主感知、決策并執行動作的軟件實體&#xff0c;具備以下特征&#xff1a;自主性&#xff1a;無需人工干預即可根據環境變化調整行為。交互性&#x…

數組實現各類數據結構

目錄 一、數組實現單鏈表 二、數組實現雙鏈表 三、數組實現棧 四、數組模擬隊列 五、數組模擬單調棧 六、數組模擬單調隊列&#xff08;滑動窗口&#xff09; 七、數組模擬堆 一、數組實現單鏈表 #include<iostream> #include<algorithm> #include<cstr…

數據處理與統計分析 —— apply自定義函數

目錄 一、向量化與偽向量化 1、向量化 2、np.vectorize 偽向量化&#xff08;特定場景&#xff09; 3、apply&#xff08;自定義函數&#xff09; 二、apply函數 1、對series中使用apply 2、對dataframe中使用apply 3、apply函數案例-泰坦尼克號數據集] 數據集下載鏈接&#xf…

如何有效利用大語言模型來智能加速產業聯盟的產業鏈轉化路徑?

觀點作者&#xff1a;科易網AI技術轉移研究院在科技創新浪潮席卷全球的今天&#xff0c;科技成果轉化已成為衡量一個國家創新能力的重要標志。然而&#xff0c;一項權威調查顯示&#xff0c;我國科技成果轉化率不足30%&#xff0c;大量有價值的創新成果仍停留在實驗室階段&…

視頻加水印 視頻加水印軟件 視頻加動態水印

如果你有一個視頻&#xff0c;你想給他加一個水印&#xff0c;那么你可以使用這個工具&#xff0c;準備好你的視頻和水印。水印一般采用PNG&#xff0c;打開這個工具&#xff0c;把你的視頻和水印拖進這個方框當中。視頻限制是MP4&#xff0c;水印限制是PNG&#xff0c;它可以把…

面向DeepSeek chat coding實錄(二)

向DeepSeek的提問 幫我設計以下兩個python class Span 屬性&#xff1a; hash值&#xff08;在init函數中通過時間初始化&#xff09; 創建時間&#xff1a;時間&#xff08;在init函數中通過時間初始化&#xff09; 結束時間&#xff1a;時間&#xff08;可選&#xff0c;默認…

Hi3516CV610-00S 海思SOC芯片 可申請開發資料

1.1 概述Hi3516CV610 是一顆應用在安防市場的 IPC SoC。在開放操作系統、新一代視頻編解碼標準、網絡安全和隱私保護、人工智能方面引領行業發展&#xff0c;主要面向室內外場景下的槍機、球機、半球機、海螺機、槍球一體機、雙目長短焦機等產品形態&#xff0c;打造極具競爭力…

算法題Day4

目錄 13. 練習13 : 整數十位 14. 練習14 : 時間轉換 15. 練習15 : 小雨的游泳時間 13. 練習13 : 整數十位 解題方法: #include <iostream> using namespace std; int a; int main() {cin >> a;cout << a % 100 / 10 << endl;return 0; } 14. 練習…

加速你的故障排查:使用 Elasticsearch 構建家電手冊的 RAG 應用

作者&#xff1a;來自 Elastic Alessandro Brofferio 學習如何使用 Elasticsearch 構建 RAG 應用&#xff0c;輕松排查你的家電問題。 想要獲得 Elastic 認證嗎&#xff1f;來看看下一次 Elasticsearch 工程師培訓什么時候開始吧&#xff01; Elasticsearch 擁有大量新功能&am…

6.Shell腳本修煉手冊---grep命令使用指南

grep 命令&#xff1a;從文本中精準篩選信息的實用指南 文章目錄grep 命令&#xff1a;從文本中精準篩選信息的實用指南一、什么是 grep&#xff1f;為什么要用它&#xff1f;二、grep 基本語法三、常用選項詳解&#xff08;附實例&#xff09;&#xff08;一&#xff09;模式選…

Python day51

浙大疏錦行 Python day51 復習日&#xff0c;DDPM class DenoiseDiffusion():def __init__(self, eps_model: nn.Module, n_steps: int, device: torch.device):super().__init__()self.eps_model eps_modelself.n_steps n_stepsself.device deviceself.beta torch.linsp…

數據結構:生成 (Generating) 一棵 AVL 樹

目錄 搭建“創世”的舞臺 注入序列&#xff0c;觀察演化 注入 10 注入 20 注入 30 注入 40 注入 50 注入 25 再次審視 上一講&#xff0c;我們已經從最根本的邏輯出發&#xff0c;推導出了 AVL 樹失衡時所必需的修復操作——旋轉 (Rotation)。 現在&#xff0c;我們將…

github 上傳代碼步驟

登錄GitHub → 點擊右上角 ?? → New Repository??。填寫倉庫名稱&#xff08;建議與本地項目同名&#xff09;&#xff0c;選擇 ??Public/Private??。??關鍵&#xff1a;不要勾選?? “Initialize with README”&#xff08;避免與本地倉庫沖突&#xff09;。點擊 …

陪診小程序系統開發:開啟智慧就醫新時代

在數字化浪潮的推動下&#xff0c;智慧醫療正逐漸成為現實。陪診小程序系統的開發&#xff0c;作為智慧醫療領域的一次重要創新&#xff0c;正以其獨特的魅力與優勢&#xff0c;引領著就醫新時代的到來。它不僅改變了傳統就醫模式&#xff0c;更以科技的力量&#xff0c;讓醫療…

朝花夕拾(七)--------從混淆矩陣到分類報告全面解析?

目錄 ??機器學習模型評估指南&#xff1a;從混淆矩陣到分類報告全面解析?? ??1. 引言?? ??2. 混淆矩陣&#xff1a;模型評估的基石?? ??2.1 什么是混淆矩陣&#xff1f;?? 2.2二分類問題的混淆矩陣 ??二分類場景下的具體案例? ?分析案例: 1.??案例…

Python讀取和設置PNG圖片的像素值

在Python中&#xff0c;可以使用Pillow庫或OpenCV庫來讀取和寫入PNG圖片的像素值。以下是兩種方法的詳細說明&#xff1a;1. 使用Pillow庫Pillow是Python中常用的圖像處理庫&#xff0c;支持多種圖像格式&#xff0c;包括PNG。讀取像素值from PIL import Imageimg Image.open(…

SkyWalking + Elasticsearch8 容器化部署指南:國內鏡像加速與生產級調優

SkyWalking Elasticsearch8 Docker 部署文檔本文提供在 Ubuntu 服務器上&#xff0c;使用 Docker Compose 部署 SkyWalking&#xff08;OAPUI&#xff09;與 Elasticsearch 8 的完整步驟&#xff0c;數據/日志落地到 /media/disk2 前置條件 Ubuntu&#xff0c;已具備 sudo 權限…

有符號和無符號的區別

有符號&#xff08;Signed&#xff09;和無符號&#xff08;Unsigned&#xff09;是計算機編程中用來描述整數數據類型能否表示負數的兩個概念。它們的主要區別在于能否表示負數以及數值的表示范圍。以下是它們的核心區別&#xff1a;1. 能否表示負數有符號&#xff08;Signed&…

8月21日作業

1、Makefile中頭文件發生過修改的解決&#xff1a; 處插入*.h依賴&#xff0c;對.h文件打的時間戳進行檢查2、頭刪和輸出//五、頭刪 void delete_head(seq_p s) {empty(s);for(int i1;i<s->len;i){s->data[i-1]s->data[i];}s->len--; }//六、輸出 void output(s…

Lucene 8.5.0 的 `.pos` 文件**邏輯結構**

Lucene 8.5.0 的 .pos 文件**邏輯結構**&#xff08;按真實實現重新整理&#xff09; .pos 文件 ├─ Header (CodecHeader) ├─ TermPositions TermCount ← 每個 term 一段&#xff0c;順序由詞典隱式決定 │ ├─ PackedPosDeltaBlock N ← 僅當 **無 payl…