圖解gpt之Seq2Seq架構與序列到序列模型

今天深入探討如何構建更強大的序列到序列模型,特別是Seq2Seq架構。序列到序列模型,顧名思義,它的核心任務就是將一個序列映射到另一個序列。這個序列可以是文本,也可以是其他符號序列。最早,人們嘗試用一個單一的RNN來搞定整個序列到序列的任務,比如直接讓RNN讀完中文句子,然后輸出英文句子。但很快發現,效果不理想。為什么呢?因為RNN在處理長序列時,就像一個記憶力有限的學者,讀到后面可能就忘了前面的內容了。梯度消失、梯度爆炸問題,以及它捕捉長距離依賴的能力不足,使得信息在處理過程中容易丟失或混淆。

編碼器-解碼器架構

為了解決這個問題,研究人員提出了序列到序列模型的基石——編碼器-解碼器架構。這個想法非常巧妙:我們不再用一個RNN來處理所有事情,而是把它分成兩個獨立的部分。

  • 一部分負責編碼器,專門負責讀取輸入序列,比如中文,然后把它壓縮成一個濃縮的精華,也就是一個固定大小的向量。
  • 另一部分負責解碼器,接收這個精華向量,然后像一個翻譯官一樣,根據這個向量,逐步生成輸出序列,比如英文。這樣分工合作,信息就更容易被完整地保存和傳遞了。

整個過程就像一個翻譯機,先把原文翻譯成一種通用語,再從通用語翻譯成目標語言。

在這里插入圖片描述

上圖更具體地展示了基于RNN或LSTM的Seq2Seq模型。左邊是輸入序列,比如咖哥喜歡小雪,后面可能加了個EOS標記表示結束。右邊是輸出序列,比如KaGe likes XiaoXue,同樣有EOS。注意**,輸入序列的開頭通常會加上一個特殊的SOS符號,表示序列開始**。解碼器在生成每個詞的時候,會依賴于之前的詞和當前的上下文狀態,所以輸出序列的開頭也用SOS標記。模型中間就是兩個RNN或LSTM單元,分別負責編碼和解碼。

我們再細看一下這兩個核心組件。

  • 編碼器,它的任務是吃進去一個完整的輸入序列,比如中文句子,然后通過內部的RNN、LSTM或者GRU等機制,一步步處理,最終吐出一個濃縮的、固定大小的向量。這個向量,我們稱之為上下文向量,它包含了整個輸入序列的所有信息。
  • 解碼器,接收這個上下文向量作為起點,然后也開始一步步處理,生成一個輸出序列。

在這里插入圖片描述

左邊是中文句子咖哥很喜歡小冰,經過編碼器處理,變成了一個向量,比如02 85 03 12 99。這個向量就是編碼器的輸出。然后,這個向量被傳遞給右邊的解碼器,解碼器根據這個向量,逐步生成英文句子KaGe very likes XiaoBing。注意,解碼器的輸出是逐個詞生成的,直到遇到EOS結束標記。整個過程就是從輸入序列到向量,再到輸出序列的完整映射。

所以,我們可以這樣總結Seq2Seq的核心思想:它本質上是一個壓縮與解壓縮的過程。輸入序列被壓縮成一個緊湊的向量,然后這個向量被解壓縮成輸出序列。這個壓縮和解壓縮的過程,通常由RNN、LSTM或GRU等序列建模方法來實現。

特別要注意的是,解碼器在生成序列時,它不是一次性生成所有詞,而是一個詞一個詞地生成,而且當前生成的詞,會作為下一個詞的輸入,這就是所謂的自回歸特性。這種特性使得模型能夠更好地處理序列的生成任務。

Seq2Seq架構有幾個非常重要的特點。

  • 非常靈活,能夠處理輸入和輸出序列長度不等的情況。比如,一個中文句子可能對應一個很長的英文句子,或者一個長句子對應一個短摘要。
  • 它組件可以替換。編碼器和解碼器可以選用不同的RNN變體,比如LSTM、GRU,甚至可以引入更復雜的結構,比如Transformer。
  • 具有很好的擴展性。我們可以在這個基礎上,進一步添加注意力機制等組件,來提升模型在處理長序列和復雜語境時的能力。

Seq2Seq模型實踐

理論講完了,我們來動手實踐一下。我們來構建一個簡單的Seq2Seq模型,讓它能夠學習如何把中文翻譯成英文。

  1. 構建實驗語料庫和詞匯表
  2. 生成Seq2Seq訓練數據
  3. 定義編碼器和解碼器類
  4. 定義Seq2Seq架構
  5. 訓練Seq2Seq架構
  6. 測試Seq2Seq架構

數據準備

數據準備。我們需要一個包含中文和英文句子對的語料庫。

sentences = [['咖哥 喜歡 小冰', '<sos> KaGe likes XiaoBing', 'KaGe likes XiaoBing <eos>'],['我 愛 學習 人工智能', '<sos> I love studying AI', 'I love studying AI <eos>'],['深度學習 改變 世界', '<sos> DL changed the world', 'DL changed the world <eos>'],['自然 語言 處理 很 強大', '<sos> NLP is so powerful', 'NLP is so powerful <eos>'],['神經網絡 非常 復雜', '<sos> Neural-Nets are complex', 'Neural-Nets are complex <eos>']]

有了語料庫,我們還需要構建詞匯表。我們需要知道每個詞對應的編號。我們分別創建一個中文詞匯表和一個英文詞匯表,把所有出現的詞都收集起來。然后,為每個詞分配一個唯一的編號,也就是索引。同時,我們還要創建一個反向索引,知道每個編號對應哪個詞。這樣,模型就能把文本轉換成數字序列,方便神經網絡處理。

中文詞匯到索引的字典: {'神經網絡': 0, '小冰': 1, '人工智能': 2, '復雜': 3, '我': 4, '處理': 5, '改變': 6, '深度學習': 7, '強大': 8, '咖哥': 9, '世界': 10, '學習': 11, '自然': 12, '語言': 13, '很': 14, '非常': 15, '愛': 16, '喜歡': 17}英文詞匯到索引的字典: {'are': 0, 'the': 1, 'NLP': 2, 'XiaoBing': 3, 'I': 4, 'so': 5, 'complex': 6, '<sos>': 7, 'studying': 8, 'KaGe': 9, 'DL': 10, 'AI': 11, 'love': 12, 'is': 13, 'world': 14, 'Neural-Nets': 15, 'changed': 16, 'powerful': 17, '<eos>': 18, 'likes': 19}

生成Seq2Seq訓練數據

我們需要把剛才的語料庫轉換成模型可以直接使用的訓練數據。具體來說,就是把每個句子的中文單詞、帶sos的英文單詞和帶eos的英文單詞都轉換成對應的索引序列。然后,把這些索引序列轉換成PyTorch的LongTensor張量格式。

原始句子: ['我 愛 學習 人工智能', '<sos> I love studying AI', 'I love studying AI <eos>']
編碼器輸入張量的形狀: torch.Size([1, 4])
解碼器輸入張量的形狀: torch.Size([1, 5])
目標張量的形狀: torch.Size([1, 5])編碼器輸入張量: tensor([[ 5, 13, 10,  4]]) # 每個詞查索引表 
解碼器輸入張量: tensor([[17, 14, 12, 18,  9]])
目標張量: tensor([[14, 12, 18,  9, 16]])

這個函數每次調用,都會返回一個批次的訓練數據。這個函數的輸出結果是這樣的:encoder_input 是中文句子的索引張量,decoder_input 是帶 <sos> 的英文句子的索引張量,target 是帶 <eos> 的英文句子的索引張量。注意 decoder_inputtarget 的區別在于 <sos><eos> 的位置。decoder_input 用于訓練解碼器,而 target 用于計算損失。這個 decoder_input 里面包含了真實的目標序列,這其實是教師強制的體現。

這里提到了一個重要的概念:教師強制。簡單來說,就是在訓練解碼器的時候,我們給它喂的是真實答案。具體來說,就是把目標序列的英文單詞,除了最后一個 <eos>,都作為輸入給解碼器。這樣做是為了讓模型更快地學習到正確的翻譯路徑。但是,這也會帶來一個問題:模型在訓練時依賴了真實答案,但在實際應用中,它只能自己生成下一個詞,這就可能造成訓練和測試時的分布不一致,也就是所謂的曝光偏差。為了解決這個問題,可以使用計劃采樣,讓模型在訓練過程中逐漸適應自己生成的詞。

定義編碼器和解碼器類

我們開始構建神經網絡模型了。

import torch.nn as nn # 導入 torch.nn 庫
# 定義編碼器類,繼承自 nn.Module
class Encoder(nn.Module):def __init__(self, input_size, hidden_size):super(Encoder, self).__init__()       self.hidden_size = hidden_size # 設置隱藏層大小       self.embedding = nn.Embedding(input_size, hidden_size) # 創建詞嵌入層       self.rnn = nn.RNN(hidden_size, hidden_size, batch_first=True) # 創建 RNN 層    def forward(self, inputs, hidden): # 前向傳播函數embedded = self.embedding(inputs) # 將輸入轉換為嵌入向量       output, hidden = self.rnn(embedded, hidden) # 將嵌入向量輸入 RNN 層并獲取輸出return output, hidden# 定義解碼器類,繼承自 nn.Module
class Decoder(nn.Module):def __init__(self, hidden_size, output_size):super(Decoder, self).__init__()       self.hidden_size = hidden_size # 設置隱藏層大小       self.embedding = nn.Embedding(output_size, hidden_size) # 創建詞嵌入層self.rnn = nn.RNN(hidden_size, hidden_size, batch_first=True)  # 創建 RNN 層       self.out = nn.Linear(hidden_size, output_size) # 創建線性輸出層    def forward(self, inputs, hidden):  # 前向傳播函數     # inputs = tensor([[ 7,  2, 13,  5, 17]])embedded = self.embedding(inputs) # 將輸入轉換為嵌入向量       output, hidden = self.rnn(embedded, hidden) # 將嵌入向量輸入 RNN 層并獲取輸出       output = self.out(output) # 使用線性層生成最終輸出return output, hiddenn_hidden = 128 # 設置隱藏層數量
# 創建編碼器和解碼器
encoder = Encoder(voc_size_cn, n_hidden)
decoder = Decoder(n_hidden, voc_size_en)編碼器結構: Encoder((embedding): Embedding(18, 128)(rnn): RNN(128, 128, batch_first=True)
)解碼器結構: Decoder((embedding): Embedding(20, 128)(rnn): RNN(128, 128, batch_first=True)(out): Linear(in_features=128, out_features=20, bias=True)
)

我們需要定義兩個類:Encoder和Decoder。這兩個類都繼承自PyTorch的nn.Module。它們的核心組件包括:

  • 嵌入層,用來把輸入的單詞索引轉換成低維的向量表示;
  • RNN層,用來處理序列信息;
  • 對于解碼器,還需要一個線性輸出層,用來把RNN的輸出轉換成最終的詞匯表概率分布。

這就是我們定義的編碼器和解碼器的代碼。可以看到

  • Encoder類主要負責處理輸入序列,它包含一個嵌入層和一個RNN層
  • Decoder類除了嵌入層和RNN層,還多了一個輸出層,用于生成最終的預測結果。
  • forward函數定義了數據如何在網絡中流動。

這里解釋一下RNN輸出的兩個值:output 和 hidden

  • output 是每個時間步的輸出,可以理解為對當前輸入的編碼。
  • hidden 是隱藏狀態,它包含了從序列開始到現在所有信息的累積,是RNN的核心記憶

在編碼器中,hidden 狀態會傳遞給解碼器作為初始狀態。而編碼器的 output,雖然在這個簡單的模型里沒直接用,但在后續的注意力機制中會發揮重要作用。解碼器的 output 則是我們最終需要的預測概率分布。

定義Seq2Seq架構

我們需要把編碼器和解碼器這兩個組件組裝起來,形成一個完整的Seq2Seq模型。

class Seq2Seq(nn.Module):def __init__(self, encoder, decoder):super(Seq2Seq, self).__init__()# 初始化編碼器和解碼器self.encoder = encoderself.decoder = decoderdef forward(self, enc_input, hidden, dec_input):    # 定義前向傳播函數# 使輸入序列通過編碼器并獲取輸出和隱藏狀態encoder_output, encoder_hidden = self.encoder(enc_input, hidden)# 將編碼器的隱藏狀態傳遞給解碼器作為初始隱藏狀態decoder_hidden = encoder_hidden# 使解碼器輸入(目標序列)通過解碼器并獲取輸出decoder_output, _ = self.decoder(dec_input, decoder_hidden)return decoder_output
# 創建 Seq2Seq 架構
model = Seq2Seq(encoder, decoder)S2S 模型結構: Seq2Seq((encoder): Encoder((embedding): Embedding(18, 128)(rnn): RNN(128, 128, batch_first=True))(decoder): Decoder((embedding): Embedding(20, 128)(rnn): RNN(128, 128, batch_first=True)(out): Linear(in_features=128, out_features=20, bias=True))
)

我們定義一個名為Seq2Seq的類,它也繼承自nn.Module。在這個類里,我們會初始化一個編碼器和一個解碼器對象。然后,定義一個forward函數,描述數據如何從輸入序列經過編碼器,得到隱藏狀態,再傳遞給解碼器,最終生成輸出序列的過程。這就是我們定義的Seq2Seq模型類代碼。

我們再來看一下這個forward函數的細節。它接收三個參數:

  • enc_input,編碼器的輸入
  • hidden,初始隱藏狀態
  • dec_input,解碼器的輸入序列

注意這個 dec_input,它包含了我們之前提到的帶 <sos> 的真實目標序列。這再次體現了教師強制的策略。函數返回的是 decoder_output,它是一個張量,每個時間步對應一個詞匯表的預測概率分布。

訓練模型

我們定義一個訓練函數,然后調用它來訓練我們的Seq2Seq模型。

def train_seq2seq(model, criterion, optimizer, epochs):for epoch in range(epochs):encoder_input, decoder_input, target = make_data(sentences) # 訓練數據的創建# encoder_input = tensor([[ 9, 17,  1]])   # decoder_input = tensor([[ 7,  9, 19,  3]])   # target = tensor([[ 9, 19,  3, 18]])hidden = torch.zeros(1, encoder_input.size(0), n_hidden) # 初始化隱藏狀態      optimizer.zero_grad()# 梯度清零        output = model(encoder_input, hidden, decoder_input) # 獲取模型輸出        loss = criterion(output.view(-1, voc_size_en), target.view(-1)) # 計算損失        if (epoch + 1) % 40 == 0: # 打印損失print(f"Epoch: {epoch + 1:04d} cost = {loss:.6f}")         loss.backward()# 反向傳播        optimizer.step()# 更新參數
# 訓練模型
epochs = 400 # 訓練輪次
criterion = nn.CrossEntropyLoss() # 損失函數
optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # 優化器
train_seq2seq(model, criterion, optimizer, epochs) # 調用函數訓練模型

可以看到,它是一個循環,每次迭代一個epoch。在每次迭代中,我們首先生成數據,初始化隱藏狀態,然后執行前向傳播、計算損失、反向傳播、更新參數這幾個步驟。我們還打印了每隔一定輪次的損失值,用來監控訓練進度。

這里解釋一下代碼中的一些細節。我們使用了nn.CrossEntropyLoss作為損失函數,因為它適合處理多分類問題,比如預測下一個詞。view函數用于改變張量的形狀,比如把輸出張量展平成二維,把目標標簽展平成一維,以便損失函數計算。我們使用了Adam優化器,這是一個非常流行的優化算法。整個訓練過程就是標準的深度學習流程。

測試模型

測試模型。我們定義一個測試函數,用來評估模型在新數據上的表現。

# 定義測試函數
def test_seq2seq(model, source_sentence):# 將輸入的句子轉換為索引encoder_input = np.array([[word2idx_cn[n] for n in source_sentence.split()]])# [[12 13  5 14  8]]# 構建輸出的句子的索引,以 '<sos>' 開始,后面跟 '<eos>',長度與輸入句子相同decoder_input = np.array([word2idx_en['<sos>']] + [word2idx_en['<eos>']]*(len(encoder_input[0])-1))# [ 7 18 18 18 18]# 轉換為 LongTensor 類型encoder_input = torch.LongTensor(encoder_input)decoder_input = torch.LongTensor(decoder_input).unsqueeze(0) # 增加一維    hidden = torch.zeros(1, encoder_input.size(0), n_hidden) # 初始化隱藏狀態    predict = model(encoder_input, hidden, decoder_input) # 獲取模型輸出    predict = predict.data.max(2, keepdim=True)[1] # 獲取概率最大的索引# 打印輸入的句子和預測的句子print(source_sentence, '->', [idx2word_en[n.item()] for n in predict.squeeze()])
# 測試模型
test_seq2seq(model, '自然 語言 處理 很 強大')

測試時,我們不再提供真實的英文句子作為解碼器的輸入,而是只提供一個sos作為起始信號

然后,我們讓模型自己一步步生成英文單詞,直到遇到eos。我們把模型預測的索引轉換回英文單詞,看看翻譯結果如何。這就是測試函數的代碼。它首先將輸入的中文句子轉換為索引。然后,它只構建了一個包含sos的解碼器輸入序列,后面填充了eos(這個代碼為了簡化,沒有實現真正的逐個token生成,而是直接輸出了與輸入序列等長的輸出)。

接著,我們初始化隱藏狀態,讓模型通過前向傳播,得到預測結果。我們取每個時間步概率最大的那個詞作為輸出,最后將這些索引轉換回英文單詞。可以看到,模型基本學到了翻譯任務。不過,這里需要指出的是,這個代碼為了簡化,沒有實現真正的逐個token生成,而是直接輸出了與輸入序列等長的輸出

對于更復雜的任務,我們需要實現更精細的生成過程,比如GPT模型那樣,一個詞一個詞地生成。

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

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

相關文章

mac M2能安裝的虛擬機和linux系統系統

能適配MAC M2芯片的虛擬機下Linux系統的搭建全是深坑&#xff0c;目前網上的資料能搜到的都是錯誤的&#xff0c;自己整理并分享給坑友們~ 網上搜索到的推薦安裝的改造過的centos7也無法進行yum操作&#xff0c;我這邊建議安裝centos8 VMware Fusion下載地址&#xff1a; htt…

「國產嵌入式仿真平臺:高精度虛實融合如何終結Proteus時代?」——從教學實驗到低空經濟,揭秘新一代AI賦能的產業級教學工具

引言&#xff1a;從Proteus到國產平臺的范式革新 在高校嵌入式實驗教學中&#xff0c;仿真工具的選擇直接影響學生的工程能力培養與創新思維發展。長期以來&#xff0c;Proteus作為經典工具占據主導地位&#xff0c;但其設計理念已難以滿足現代復雜系統教學與國產化技術需求。…

【Linux】在Arm服務器源碼編譯onnxruntime-gpu的whl

服務器信息&#xff1a; aarch64架構 ubuntu20.04 nvidia T4卡 編譯onnxruntime-gpu前置條件&#xff1a; 已經安裝合適的cuda已經安裝合適的cudnn已經安裝合適的cmake 源碼編譯onnxruntime-gpu的步驟 1. 下載源碼 git clone --recursive https://github.com/microsoft/o…

前端上傳el-upload、原生input本地文件pdf格式(純前端預覽本地文件不走后端接口)

前端實現本地文件上傳與預覽&#xff08;PDF格式展示&#xff09;不走后端接口 實現步驟 第一步&#xff1a;文件選擇 使用前端原生input上傳本地文件&#xff0c;或者是el-upload組件實現文件選擇功能&#xff0c;核心在于文件渲染處理。&#xff08;input只不過可以自定義樣…

Python 數據分析與可視化:開啟數據洞察之旅(5/10)

一、Python 數據分析與可視化簡介 在當今數字化時代&#xff0c;數據就像一座蘊藏無限價值的寶藏&#xff0c;等待著我們去挖掘和探索。而 Python&#xff0c;作為數據科學領域的明星語言&#xff0c;憑借其豐富的庫和強大的功能&#xff0c;成為了開啟這座寶藏的關鍵鑰匙&…

C語言學習記錄——深入理解指針(4)

OK&#xff0c;這一篇主要是講我學習的3種指針類型。 正文開始&#xff1a; 一.字符指針 所謂字符指針&#xff0c;顧名思義就是指向字符的指針。一般寫作 " char* " 直接來說說它的使用方法吧&#xff1a; &#xff08;1&#xff09;一般使用情況&#xff1a; i…

springboot3+vue3融合項目實戰-大事件文章管理系統獲取用戶詳細信息-ThreadLocal優化

一句話本質 為每個線程創建獨立的變量副本&#xff0c;實現多線程環境下數據的安全隔離&#xff08;線程操作自己的副本&#xff0c;互不影響&#xff09;。 關鍵解讀&#xff1a; 核心機制 ? 同一個 ThreadLocal 對象&#xff08;如示意圖中的紅色區域 tl&#xff09;被多個線…

Nacos源碼—8.Nacos升級gRPC分析六

大綱 7.服務端對服務實例進行健康檢查 8.服務下線如何注銷注冊表和客戶端等信息 9.事件驅動架構源碼分析 一.處理ClientChangedEvent事件 也就是同步數據到集群節點&#xff1a; public class DistroClientDataProcessor extends SmartSubscriber implements DistroDataSt…

設計雜談-工廠模式

“工廠”模式在各種框架中非常常見&#xff0c;包括 MyBatis&#xff0c;它是一種創建對象的設計模式。使用工廠模式有很多好處&#xff0c;尤其是在復雜的框架中&#xff0c;它可以帶來更好的靈活性、可維護性和可配置性。 讓我們以 MyBatis 為例&#xff0c;來理解工廠模式及…

AI與IoT攜手,精準農業未來已來

AIoT&#xff1a;農業領域的變革先鋒 在科技飛速發展的當下&#xff0c;人工智能&#xff08;AI&#xff09;與物聯網&#xff08;IoT&#xff09;的融合 ——AIoT&#xff0c;正逐漸成為推動各行業變革的關鍵力量&#xff0c;農業領域也不例外。AIoT 技術通過將 AI 的智能分析…

排錯-harbor-db容器異常重啟

排錯-harbor-db容器異常重啟 環境&#xff1a; docker 19.03 , harbor-db(postgresql) goharbor/harbor-db:v2.5.6 現象&#xff1a; harbor-db 容器一直restart&#xff0c;查看日志發現 報錯 initdb: error: directory "/var/lib/postgresql/data/pg13" exists…

Docker容器啟動失敗?無法啟動?

Docker容器無法啟動的疑難雜癥解析與解決方案 一、問題現象 Docker容器無法啟動是開發者在容器化部署中最常見的故障之一。盡管Docker提供了豐富的調試工具&#xff0c;但問題的根源往往隱藏在復雜的配置、環境依賴或資源限制中。本文將從環境變量配置錯誤這一細節問題入手&am…

查看購物車

一.查看購物車 查看購物車使用get請求。我們要查看當前用戶的購物車&#xff0c;就要獲取當前用戶的userId字段進行條件查詢。因為在用戶登錄時就已經將userId封裝在token中了&#xff0c;因此我們只需要解析token獲取userId即可&#xff0c;不需要前端再傳入參數了。 Control…

C/C++ 內存管理深度解析:從內存分布到實踐應用(malloc和new,free和delete的對比與使用,定位 new )

一、引言&#xff1a;理解內存管理的核心價值 在系統級編程領域&#xff0c;內存管理是決定程序性能、穩定性和安全性的關鍵因素。C/C 作為底層開發的主流語言&#xff0c;賦予開發者直接操作內存的能力&#xff0c;卻也要求開發者深入理解內存布局與生命周期管理。本文將從內…

使用Stable Diffusion(SD)中CFG參數指的是什么?該怎么用!

1.定義&#xff1a; CFG參數控制模型在生成圖像時&#xff0c;對提示詞&#xff08;Prompt&#xff09;的“服從程度”。 它衡量模型在“完全根據提示詞生成圖像”和“自由生成圖像”&#xff08;不參考提示詞&#xff09;之間的權衡程度。 數值范圍&#xff1a;常見范圍是 1 …

【GESP】C++三級練習 luogu-B2156 最長單詞 2

GESP三級練習&#xff0c;字符串練習&#xff08;C三級大綱中6號知識點&#xff0c;字符串&#xff09;&#xff0c;難度★★☆☆☆。 題目題解詳見&#xff1a;https://www.coderli.com/gesp-3-luogu-b2156/ 【GESP】C三級練習 luogu-B2156 最長單詞 2 | OneCoderGESP三級練…

Linux網絡基礎 -- 局域網,廣域網,網絡協議,網絡傳輸的基本流程,端口號,網絡字節序

目錄 1. 計算機網絡背景 1.1 局域網 1.1.2 局域網的組成 1.2 廣域網 1.1.2 廣域網的組成 2. 初始網絡協議 2.1 網絡協議的定義和作用 2.2 網絡協議的分層結構 2.2.1 OSI七層模型 2.2.2 TCP/IP 五層&#xff08;四層&#xff09;模型 3. 再識網絡協議 3.1 為什么要有…

【PostgreSQL】超簡單的主從節點部署

1. 啟動數據庫 啟動主節點 docker run --name postgres-master -e POSTGRES_PASSWORDmysecretpassword -p 5432:5432 -d postgres啟動從節點 docker run --name postgres-slave -e POSTGRES_PASSWORDmysecretpassword -p 5432:5432 -d postgres需要配置掛載的存儲卷 2. 數據…

c#修改ComboBox當前選中項的文本

對于一個C#的Combobox列表&#xff0c;類型設置為下拉樣式&#xff0c;不允許輸入&#xff0c;只能選擇&#xff0c;樣子如下&#xff1a; 該控件的名字為 cbb1&#xff0c;如果要修改當前這個“A1區”的文件&#xff0c;則用如下方式&#xff1a; cbb1.Items[cbb1.SelectedInd…

Java設計模式之適配器模式:從入門到精通

適配器模式(Adapter Pattern)是Java中最常用的結構型設計模式之一,它像一座橋梁連接兩個不兼容的接口,使得原本由于接口不兼容而不能一起工作的類可以協同工作。本文將全面深入地解析適配器模式,從基礎概念到高級應用,包含豐富的代碼示例、詳細注釋、使用場景分析以及多維對…