從零開始實現大語言模型(二):文本數據處理

1. 前言

神經網絡不能直接處理自然語言文本,文本數據處理的核心是做tokenization,將自然語言文本分割成一系列tokens。

本文介紹tokenization的基本原理,OpenAI的GPT系列大語言模型使用的tokenization方法——字節對編碼(BPE, byte pair encoding),并構建訓練大語言模型的DatasetDataLoader

2. Tokenization

Tokenization的目標是將自然語言文本分割成一系列tokens。在英文自然語言處理領域,最常用的tokenization方法是將英文文本分割成單詞及標點符號。在中文自然語言處理領域,往往會將一個字或一個標點符號作為一個token。

如下面的代碼所示,可以使用Python自帶的正則表達式庫re將英文文本分割成不同的單詞及標點符號:

import resentence = "Hello, world. This, is a test."
tokens = re.split(r'([,.?_!"()\']|\s)', sentence)
tokens = [item for item in tokens if item.strip()]
print(tokens)

執行上面代碼,打印結果如下:

['Hello', ',', 'world', '.', 'This', ',', 'is', 'a', 'test', '.']

上面的tokenization示例代碼刪除掉了文本中的空格。在實際應用中,也可以將空格視為一個單獨的字符。刪除空格可以顯著減少分割文本產生的tokens數量,減少內存消耗,降低計算資源需求。但是在構建代碼生成模型等應用場景中,必須將文本中的空格視為一個單獨的字符,并編碼成相應的token。

3. 將Token轉換成數字ID

絕大部分處理自然語言文本的神經網絡都包含Embedding層(embedding layer)torch.nn.Embedding。Embedding層存儲了不同tokens對應的embedding向量,輸入一系列tokens對應的索引列表,Embedding層輸出相應tokens對應的embedding向量。

對自然語言文本做tokenization,將文本分割成不同的tokens。Tokens不能直接輸入神經網絡的Embedding層,需要將tokens轉換成數字ID(token ID)。將tokens轉換成數字ID首先要構造詞匯表(vocabulary)。詞匯表定義了不同tokens與數字索引的一一對應關系,可以使用詞匯表將分割自然語言文本產生的一系列tokens轉換成數字ID列表,也可以通過詞匯表將數字ID列表還原成自然語言文本。

如下圖所示,構造詞匯表需要對訓練數據集中全部文本數據做tokenization,獲取所有不同的tokens,并一一添加到字典(dict)中。字典的key為訓練數據集中的不同tokens,字典的value為相應token添加到字典中的順序。

圖一

可以使用如下代碼構造詞匯表:

diff_tokens = sorted(set(tokens))
vocabulary = {token: idx for idx, token in enumerate(diff_tokens)}for item in vocabulary.items():print(item)

執行上面代碼,打印結果如下:

(',', 0)
('.', 1)
('Hello', 2)
('This', 3)
('a', 4)
('is', 5)
('test', 6)
('world', 7)

4. 常用的特殊Tokens

上述對訓練數據集中全部文本數據做tokenization,獲取所有不同的tokens,并構造將token映射成數字ID的詞匯表的文本數據處理方法無法將訓練數據集中不存在的token轉換成數字ID。在自然語言處理項目實踐中,往往會在詞匯表中增加一些特殊tokens,以增強模型理解自然語言文本結構等信息的能力。常用的特殊tokens如下所示:

  • <|unk|>(unknown):該token一般用于表示不在構建的詞匯表中的單詞或字符
  • <|endoftext|>(end of text):該token一般用于分割兩個不相關的文本。訓練大語言模型的文本數據一般由許多文本拼接而成,不同文本之間使用<|endoftext|>分隔,使大語言模型可以理解訓練數據的組織方式
  • [BOS](beginning of sequence):該token通常位于一段文本的開頭,用于給模型提供輸入文本的組織結構信息
  • [EOS](end of sequence):該token通常位于一段文本的末尾,用于拼接多個不相關的文本,其作用與<|endoftext|>類似
  • [PAD](padding):訓練大語言模型每次會使用一個batch的訓練樣本,構成一個張量(tensor),張量內所有訓練樣本token數量必須相同。如果batch中包含不同長度的訓練樣本,一般會使用[PAD]將較短的訓練樣本填充至batch中最長訓練樣本長度

GPT-2系列大語言模型只在詞匯表中增加了<|endoftext|>這個特殊token。其使用了一種被稱為字節對編碼(byte pair encoding)的tokenization方法,該方法分割文本不會產生詞匯表不包含的新token。

可以將上述tokenization方法封裝成一個Tokenizer類,其中encode函數用于將自然語言文本分割成一系列tokens,并轉換成數字ID列表。decode函數用于將數字ID列表還原成自然語言文本:

class Tokenizer:def __init__(self, vocabulary):self.str_to_int = vocabularyself.int_to_str = list(vocabulary.keys())def encode(self, text):preprocessed = re.split(r'([,.?_!"()\']|\s)', text)preprocessed = [item for item in preprocessed if item.strip()]preprocessed = [item if item in self.str_to_int else "<|unk|>" for item in preprocessed]ids = [self.str_to_int[s] for s in preprocessed]return idsdef decode(self, ids):text = " ".join([self.int_to_str[i] for i in ids])text = re.sub(r'\s+([,.?!"()\'])', r'\1', text)return text

可以使用如下代碼實例化Tokenizer類對象,并調用encodedecode函數,打印示例文本對應的tokens及相應文本內容:

vocabulary.update({"<|unk|>": len(vocabulary), "<|endoftext|>": len(vocabulary) + 1})
text = "Hello world. <|endoftext|> This is a test dataset."tokenizer = Tokenizer(vocabulary)
print(tokenizer.encode(text))
print(tokenizer.decode(tokenizer.encode(text)))

執行上面代碼,打印結果如下:

[2, 7, 1, 9, 3, 5, 4, 6, 8, 1]
Hello world. <|endoftext|> This is a test <|unk|>.

5. 字節對編碼(Byte Pair Encoding)

字節對編碼(BPE)是大語言模型GPT-2、GPT-3以及ChatGPT使用的tokenization方法。BPE會將所有單個字符(如"a","b"等)以及頻繁出現的字符組合(subtoken)添加到詞匯表中(如字符"d"和"e"的組合"de"在許多英文單詞中很常見,因此會將"de"添加到詞匯表中作為一個token)。

如下圖所示,BPE可以將不在其詞匯表中的單詞分解成粒度更小的字符或字符組合,因此OpenAI的GPT系列模型不用<|unk|>等特殊token來處理不在其詞匯表中的單詞。

圖二

OpenAI開源了其使用Rust語言實現的非常高效的BPE算法tiktoken庫,可以使用如下命令安裝tiktoken:

!pip install tiktoken==0.5.1

使用tiktoken.encoding_for_model方法創建tokenizer對象,加載大語言模型GPT-2所使用的詞匯表,并調用encodedecode函數,測試BPE算法:

import tiktokentokenizer = tiktoken.encoding_for_model("gpt2")
text = "Hello, do you like tea? <|endoftext|> In the sunlit terraces of someunknownPlace."
integers = tokenizer.encode(text, allowed_special={"<|endoftext|>"})
print(integers)
strings = tokenizer.decode(integers)
print(strings)

執行上面代碼,打印結果如下:

[15496, 11, 466, 345, 588, 8887, 30, 220, 50256, 554, 262, 4252, 18250, 8812, 2114, 286, 617, 34680, 27271, 13]
Hello, do you like tea? <|endoftext|> In the sunlit terraces of someunknownPlace.

創建tokenizer對象并加載大語言模型GPT-3.5所使用的詞匯表,可以使用tokenizer = tiktoken.encoding_for_model("gpt-3.5-turbo")方法。加載大語言模型GPT-4所使用的詞匯表,可以使用tokenizer = tiktoken.encoding_for_model("gpt-4")

查看tiktoken的內部源碼可知,大語言模型GPT-3.5及GPT-4所使用的詞匯表完全相同。

6. 構建訓練大語言模型的Dataset及DataLoader

6.1 大語言模型的訓練樣本

預訓練大語言模型的數據集包含許多自然語言文檔,不同文檔的內容使用<|endoftext|>拼接,構成一個非常長的文本字符串,使用上述BPE算法可以將該字符串轉變成數字ID列表。

使用下一個token預測任務預訓練大語言模型,從數字ID列表中隨機抽取一個長度為context_len的子列表輸入大語言模型。大語言模型可以執行context_len次下一個token預測任務,共預測輸出context_len個tokens。

如下圖所示,假設context_len等于8,則第一次下一個token預測任務大模型根據輸入LLMs預測learn,第二次根據輸入LLMs learn預測to。依此類推,第八次根據輸入LLMs learn to predict one word at a預測time

圖三

在實際訓練大語言模型時,會輸入長度為context_len的數字ID列表,每個數字對應一個token,而不是輸入一個字符串。

因此可以使用如下規則構建訓練大語言模型的訓練樣本。每個訓練樣本包含一個input-target pair,其中:

  • input:從訓練數據集對應的數字ID列表中隨機抽取的長度為context_len的子列表
  • target:將input子列表向后移一個token對應的子列表,其中target中第 i i i個token ID為input中第 i ? 1 i-1 i?1個token ID

理論上,長度為 n n n的token ID列表對應的訓練數據集最多可以構造出 n ? context_len + 1 n-\text{context\_len}+1 n?context_len+1個不同的訓練樣本。實際上,會使用stride控制兩個相鄰訓練樣本之間的重疊tokens數量。如果 stride = = 1 \text{stride}==1 stride==1,則兩個相鄰的訓練樣本存在個 context_len ? 1 \text{context\_len}-1 context_len?1重疊的tokens,第 i i i個訓練樣本的target即為第 i + 1 i+1 i+1個訓練樣本的input。如果 stride = = context_len \text{stride}==\text{context\_len} stride==context_len,則兩個相鄰訓練樣本之間不存在重疊的tokens。stride越小,則可以構造出更多訓練樣本,但是模型更容易過擬合。stride越大,模型越不容易過擬合,但是同一個訓練數據集中構造的訓練樣本數會更少。

6.2 構建Dataset

Pytorch提供的高效組織及加載訓練數據的模塊由DatasetDataLoader類構成,其中Dataset類用于定義如何加載每個訓練樣本,DataLoader類用于隨機打亂訓練數據集,并將訓練數據集劃分到不同的batch。

構建訓練數據集對應的Dataset,需要實現一個Dataset的子類,并重寫__init__構造方法,__getitem__方法,以及__len__方法。__init__方法用于初始化與訪問訓練數據相關的屬性,如文件路徑、數據庫連接器等等。如果訓練數據集不是特別大,可以在__init__方法中將整個訓練數據集全部讀取到內存中。如果訓練數據集特別大,無法一次全部加載進內存,一般只會在__init__方法中初始化數據文件路徑,后續實際要用到相應數據時才會去讀取。__getitem__中實現了根據索引返回相應訓練樣本的方法。在DataLoader類的實例化對象獲取一個batch的訓練樣本時,__getitem__方法會被調用batch_size次,共傳入batch_size個不同的索引,返回batch_size個不同的訓練樣本,構成一個訓練大語言模型的batch。__len__方法定義了訓練數據集的大小,假設__len__方法返回訓練數據集的大小為10,則每次調用__getitem__方法傳入的索引均為0-9之間(包括0和9)的整數。

綜上所述,可以使用如下代碼構建訓練大語言模型的Dataset

import os
import json
import randomimport torch
import tiktoken
from torch.utils.data import Datasetclass LLMDataset(Dataset):def __init__(self, data_path, file_index_dir, vocabulary, special_token_id, context_len, stride):self.tokenizer = tiktoken.encoding_for_model(vocabulary)self.special_token_id = special_token_idself.context_len = context_lenself.stride = strideif not os.path.exists(file_index_dir):os.makedirs(file_index_dir, exist_ok=True)file_index_path = os.path.join(file_index_dir, "file_index.json")if not os.path.isfile(file_index_path):support_file_paths = []for root, dirs, files in os.walk(data_path):for file in files:if file.endswith((".txt", ".md")):file_path = os.path.join(root, file)support_file_paths.append(file_path)random.shuffle(support_file_paths)file_indexes, total_tokens = dict(), 0for file_path in support_file_paths:with open(file_path, "rt", encoding="utf-8") as f:file_token = self.tokenizer.encode(f.read(), allowed_special=self.tokenizer.special_tokens_set)file_indexes[file_path] = [total_tokens, total_tokens + len(file_token)]total_tokens += len(file_token) + 1with open(file_index_path, "wt", encoding="utf-8") as f:json.dump(file_indexes, f, ensure_ascii=False)with open(file_index_path, "rt", encoding="utf-8") as f:self.file_index = json.load(f)max_token_index = list(self.file_index.values())[-1][1]self.num_sample = (max_token_index - context_len) // stride + 1def __getitem__(self, index):index_files = self._index_files(index)start_index = index * self.stridestop_index = start_index + self.context_lensample = []for file_path in index_files:index_range = self.file_index[file_path]file_start, file_end = 0, index_range[1] - index_range[0] + 1if index_range[0] <= start_index <= index_range[1]:file_start = start_index - index_range[0]if index_range[0] <= stop_index <= index_range[1]:file_end = stop_index - index_range[0] + 1with open(file_path, "rt", encoding="utf-8") as f:tokens = self.tokenizer.encode(f.read(), allowed_special=self.tokenizer.special_tokens_set)tokens.append(self.special_token_id)sample.extend(tokens[file_start: file_end])return torch.tensor(sample[:-1]), torch.tensor(sample[1:])def __len__(self):return self.num_sampledef _index_files(self, index):index_files = []start_file, stop_file = None, Nonestart_index = index * self.stridestop_index = start_index + self.context_lenfor file_path, index_range in self.file_index.items():if index_range[0] <= start_index <= index_range[1]:start_file = file_pathif start_file is not None:index_files.append(file_path)if index_range[0] <= stop_index <= index_range[1]:stop_file = file_pathif stop_file is not None:breakreturn index_files

LLMDataset類的__init__方法中通過self.tokenizer = tiktoken.encoding_for_model(vocabulary)初始化了將文本轉換為token ID的BPE tokenizer。遍歷整個訓練數據集中的.txt.md文件,初始化了一個key為文件路徑,value為文件中全部token的[起始索引, 終止索引]列表的字典self.file_index,用于在__getitem__方法中根據索引找到相應訓練樣本所在文件。初始化了記錄訓練數據集中可構造訓練樣本總數的變量self.num_sample

__getitem__方法調用self._index_files(index)函數,返回index對應的訓練樣本所分布的文件路徑。遍歷文件路徑列表index_files,從各個文件中取出屬于index對應訓練樣本部分的tokens,并組合成訓練樣本sample

上述訓練大語言模型的LLMDataset并沒有將訓練數據一次全部加載進內存,只在__init__方法中記錄了訓練數據文件,并在調用__getitem__方法時根據index實時讀取所需文件,構造訓練樣本。這種方法可以不用將全部訓練數據加載進內存,但是需要耗費一定時間完成訓練樣本構造。

構建訓練大語言模型的DataLoader時,可以通過設置num_workers,使數據讀取與模型訓練并行進行。只要LLMDataset中的訓練數據構造效率不是特別慢,一般不會影響模型訓練效率。

如果訓練大語言模型的計算服務器集群內存足夠大到可以將整個訓練數據集一次性全部加載進內存,構建訓練大語言模型的Dataset時,可以在__init__方法中讀取訓練數據集中的全部.txt.md文檔,將不同文檔的內容使用<|endoftext|>拼接,構成一個非常長的文本字符串,并使用BPE tokenizer分別將該字符串轉變成token ID列表,存入計算服務器集群的內存。

雖然可以在構建訓練大語言模型的DataLoader時,通過設置num_workers,使數據讀取與模型訓練并行進行,一定程度上避免訓練數據構造效率對模型訓練的影響。但是在內存資源充足的情況下,直接在__init__方法中將整個訓練數據集全部加載進內存,從而提升__getitem__方法中根據指定index構造訓練數據的速度,可以使模型訓練的整體效率至少不比使用上面構建的Dataset差。具體代碼如下所示:

class LLMDataset(Dataset):def __init__(self, data_path, vocabulary, special_token_id, context_len, stride):self.context_len = context_lenself.stride = stridesupport_file_paths = []for root, dirs, files in os.walk(data_path):for file in files:if file.endswith((".txt", ".md")):file_path = os.path.join(root, file)support_file_paths.append(file_path)random.shuffle(support_file_paths)self.tokens = []tokenizer = tiktoken.encoding_for_model(vocabulary)for file_path in support_file_paths:with open(file_path, "rt", encoding="utf-8") as f:file_token = tokenizer.encode(f.read(), allowed_special=tokenizer.special_tokens_set)file_token.append(special_token_id)self.tokens.extend(file_token)self.num_sample = (len(self.tokens) - context_len - 1) // stride + 1def __getitem__(self, index):start_index = index * self.stridex = self.tokens[start_index: start_index + self.context_len]y = self.tokens[start_index + 1: start_index + self.context_len + 1]return torch.tensor(x), torch.tensor(y)def __len__(self):return self.num_sample

6.3 構建DataLoader

構建分batch讀取訓練數據的DataLoader,只需要傳入一個Dataset對象,并實例化DataLoader類對象。可以使用如下代碼構建訓練大語言模型的DataLoader

from torch.utils.data import DataLoaderbatch_size = 16
random_seed = 123torch.manual_seed(random_seed)dataset = LLMDataset(data_path="some_data_folder_path")
train_loader = DataLoader(dataset=dataset,batch_size=batch_size,shuffle=True,num_workers=4,drop_last=True
)

shuffle參數用于控制是否隨機打亂訓練數據集。如果shuffle設置為FalseDataLoader會依據索引從小到大的順序依次生成不同batch的訓練數據。如果shuffle設置為TrueDataLoader會隨機打亂所有訓練樣本的索引,并按照隨機打亂后的索引順序依次生成不同batch的訓練數據。一般會將訓練數據集對應DataLoadershuffle參數設置為True,確保不同batch的訓練數據是獨立同分布的。測試數據集對應DataLoadershuffle參數一般會設置為False,因為測試數據集中數據不被用于訓練模型,保存數據測試順序信息有助于分析數據測試結果。

通過torch.manual_seed(random_seed)指定隨機數種子,可以使DataLoader在不同次訓練流程中生成完全相同的訓練樣本索引隨機排列,但是一次訓練流程的對訓練數據集的不同次迭代中,訓練樣本索引的隨機排列會各不相同。設置隨機數種子有助于神經網絡訓練結果復現,但是不會使得在訓練過程中陷入重復的更新周期。

假設訓練數據集共包含5個不同的訓練樣本,構建DataLoader時設置shuffle參數為True,則在一次訓練流程的前3次對訓練數據集的遍歷過程中,訪問訓練數據的順序可能如下(不同隨機數種子會產生不同的訪問順序):

  • 第一次遍歷訓練數據集的順序:[3, 4, 1, 0, 2]
  • 第二次遍歷訓練數據集的順序:[2, 1, 0, 3, 4]
  • 第三次遍歷訓練數據集的順序:[1, 4, 0, 3, 2]

保持隨機數種子不變,第二次執行訓練代碼,在第二次訓練流程中的前3次對訓練數據集的遍歷順序必定與上面的遍歷順序相同。

如果在訓練大語言模型時程序異常中斷,從保存的斷點(checkpoint)處恢復訓練環境,需要特別注意隨機數種子的設置與變更。如果從某個batch對應的checkpoint恢復訓練環境,只需要使用同一個隨機數種子,并跳過前 k k k個已經訓練的batch即可。如果從某個epoch對應的checkpoint處繼續訓練模型,需要變更隨機數種子,確保新的一輪訓練遍歷訓練數據集的順序與上一次遍歷訓練數據集的順序不一致。在訓練大語言模型時,建議不同epoch使用不同的隨機數種子,并記錄隨機數種子的使用順序。

batch_size是指訓練大語言模型的一個batch中包含訓練樣本的數量。batch_size越小,則訓練大語言模型要求的顯卡最大內存越小,但是會導致計算出的更新大語言模型的梯度方差較大,影響大語言模型訓練時的收斂速度及模型最終效果。batch_size的設置可以參考OpenAI訓練GPT系列大語言模型的論文,或者設置成當前顯卡內存資源允許的最大值。

在實際訓練大語言模型時,訓練數據集中的訓練樣本數量一般不太可能恰好構成整數個batch,最后一個batch很可能僅包含相對非常少的訓練樣本。在一個訓練的epoch中,使用包含訓練樣本數量非常少的batch作為最后一個batch會引入一次噪聲較大的更新梯度,影響訓練大語言模型時的收斂效果。將drop_last設置為True,會將每個epoch中的最后一個batch丟棄,不參與模型參數更新。

num_workers參數用于控制數據并行加載及預處理。如下圖所示,在使用GPU訓練大語言模型時,CPU不僅要與GPU交互處理深度學習模型參數調度等任務,還要加載及預處理訓練數據。如果num_workers=0,系統將使用主進程加載數據,數據處理與GPU任務調度時串行的,GPU在CPU加載及預處理訓練數據時處于空閑狀態,會明顯降低模型訓練速度及GPU利用率。如果num_workers大于0,系統將啟動多個工作進程并行加載及預處理訓練數據,使主進程專注于GPU資源及訓練任務調度。num_workers必須根據系統計算資源及訓練數據集情況來確定,根據實踐經驗,大部分情況下將num_workers設置為4可以比較高效地利用系統計算資源。

圖四

7. 結束語

本文詳細講解了文本數據處理的方法,并構建了訓練大語言模型的DatasetDataLoader。請坐好站穩,我們將要去深入了解大語言模型的神經網絡架構了!

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

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

相關文章

重采樣(上采樣或下采樣)是什么?

重采樣&#xff08;Resampling&#xff09;是在數據處理中常用的一種技術&#xff0c;主要用于處理數據集中的不平衡問題。具體來說&#xff0c;重采樣可以分為上采樣&#xff08;Oversampling&#xff09;和下采樣&#xff08;Undersampling&#xff09;&#xff0c;它們分別是…

【bug報錯已解決】ERROR: Could not find a version that satisfies the requirement

&#x1f3ac; 鴿芷咕&#xff1a;個人主頁 &#x1f525; 個人專欄: 《C干貨基地》《粉絲福利》 ??生活的理想&#xff0c;就是為了理想的生活! 文章目錄 引言一、問題描述1.1 報錯示例1.2 報錯分析 二、解決方法2.1 方法一2.2 方法二 三、總結 引言 有沒有遇到過那種讓人…

軟件開發中常用環境你都知道哪些?

目錄 本地環境&#xff08;Local Environment&#xff0c;簡稱 LOCAL&#xff09; 開發環境&#xff08;Development Environment&#xff0c;簡稱 DEV&#xff09; 測試環境&#xff08;Testing Environment&#xff0c;簡稱 TEST&#xff09; 集成測試環境&#xff08;Sy…

墨烯的C語言技術棧-C語言基礎-003

三.數據類型 1.char // 字符數據型 2.short // 短整型 3.int // 整型 4.long // 長整型 5.long long // 更長的整型 6.float // 單精度浮點數 7.double // 雙精度浮點數 為什么寫代碼? 為了解決生活中的問題 購物,點餐,看電影 為什么有這么多類型呢? 因為說的話都是字符型…

CM-UNet: Hybrid CNN-Mamba UNet for Remote Sensing Image Semantic Segmentation

論文&#xff1a;CM-UNet: Hybrid &#xff1a;CNN-Mamba UNet for Remote Sensing Image Semantic Segmentation 代碼&#xff1a;https://github.com/XiaoBuL/CM-UNet Abstrcat: 由于大規模圖像尺寸和對象變化&#xff0c;當前基于 CNN 和 Transformer 的遙感圖像語義分割方…

mysql 中 單獨獲取已知日期的年月日其中之一

限定條件&#xff1a;2021年8月&#xff0c;寫法有很多種&#xff0c;比如用year/month函數的year(date)2021 and month(date)8&#xff0c;比如用date_format函數的date_format(date, "%Y-%m")"202108"每天&#xff1a;按天分組group by date題目數量&…

java之靜態屬性方法

在java中有一個static的關鍵字&#xff0c;它用來修飾類的成員。如果用static修飾屬性&#xff0c;該屬性被稱為靜態屬性 靜態屬性的訪問格式如下 類名.屬性名 如果沒有修飾靜態屬性示例代碼如下 class Xuesheng1{String name;int age;String school"A大學";publ…

openGauss真的比PostgreSQL差了10年?

前不久寫了MogDB針對PostgreSQL的兼容性文章&#xff0c;我在文中提到針對PostgreSQL而言&#xff0c;MogDB兼容性還是不錯的&#xff0c;其中也給出了其中一個能源客戶之前POC的遷移報告數據。 But很快我發現總有人回留言噴我&#xff0c;而且我發現每次噴的這幫人是根本不看文…

2024廣州智能音箱展|廣州藍牙耳機展

2024廣州智能音箱展|廣州藍牙耳機展 時間&#xff1a;2024年11月29日-12月1日 地點&#xff1a;廣州琶洲保利世貿博覽館 【展會簡介】 中國是全球最大的音頻產品制造基地和消費市場&#xff0c;隨著國內外互聯網巨頭紛紛瞄準音頻行業并投入巨資布局AI產品矩陣&#xff0c;音…

pom.xml文件加載后沒有變成maven圖標

原因&#xff1a; 開啟了IDEA的節電模式 現象為&#xff1a; xml會變橙色&#xff0c;yml變粉色&#xff0c;自動提示關閉等 把這個節能模式的勾選給取消掉就可以正常顯示了

python提取圖片中的文字寫入excel文件,并打包為exe可執行文件

python提取圖片數據寫入excel&#xff0c;并打包為exe可執行文件 1. 以下面的圖片為例2. python環境需要的依賴包3. 創建交互式窗口4. 讀取文件夾下的所有文件并提取數據5. 提取圖片中字段的代碼6. 打包代碼為exe可執行文件安裝打包依賴文件運行打包代碼 1. 以下面的圖片為例 2…

入門Salesforce:必須掌握的20+基礎專業術語!

Salesforce的發展令人印象深刻。在過去的20年中&#xff0c;Salesforce創建了一個由管理員、開發人員、顧問和用戶組成的生態系統&#xff0c;不斷顛覆創新CRM&#xff0c;促進平等和多樣性。 作為初學者&#xff0c;探索Salesforce領域就像學習一門新語言。Salesforce中有著大…

Postman環境變量秘籍:pm.environment的高級使用指南

&#x1f4d3; Postman環境變量秘籍&#xff1a;pm.environment的高級使用指南 Postman是API開發和測試的強大工具&#xff0c;它提供了豐富的功能來簡化和加速開發過程。pm.environment 是Postman中用于管理環境變量的內置對象&#xff0c;它允許你在集合運行時存儲和訪問環境…

YOLOv8改進 | 卷積模塊 | 減少冗余計算和內存訪問的PConv【CVPR2023】

秋招面試專欄推薦 &#xff1a;深度學習算法工程師面試問題總結【百面算法工程師】——點擊即可跳轉 &#x1f4a1;&#x1f4a1;&#x1f4a1;本專欄所有程序均經過測試&#xff0c;可成功執行&#x1f4a1;&#x1f4a1;&#x1f4a1; 專欄目錄 &#xff1a;《YOLOv8改進有效…

Vue3詳解

vite和webpack區別 vite vite使用原生ES模塊進行開發&#xff0c;無需在編譯時將所有代碼轉換為JS打包&#xff0c;從而提供了更快的熱更新和自動刷新功能&#xff1b; vite在開發模式下沒有打包步驟&#xff0c;而是利用瀏覽器的ES Module Imports特性實現按需編譯&#xff…

深入分析 Android HTTPS 證書管理策略:設置本地證書、使用系統默認證書和忽略證書

深入分析 Android HTTPS 證書管理策略&#xff1a;設置本地證書、使用系統默認證書和忽略證書 在 Android 應用開發中&#xff0c;確保 HTTPS 請求的安全性至關重要。為實現這一目標&#xff0c;我們可以通過不同的方式來管理 HTTPS 證書。本文將詳細探討三種常見的證書管理策…

【ajax實戰08】分頁功能

本文章目標&#xff1a;點擊上/下一頁按鈕&#xff0c;實現對應頁面的變化 實現基本步驟&#xff1a; 一&#xff1a;保存并設置文章總條數 設置一個全局變量&#xff0c;將服務器返回的數據返回給全局變量 二&#xff1a;點擊下一頁&#xff0c;做臨界值判斷&#xff0c;并…

Firefox 編譯指南2024 Windows10篇- 編譯Firefox(三)

1.引言 在成功獲取了Firefox源碼之后&#xff0c;下一步就是將這些源碼編譯成一個可執行的瀏覽器。編譯是開發流程中的關鍵環節&#xff0c;通過編譯&#xff0c;我們可以將源代碼轉換為可執行的程序&#xff0c;測試其功能&#xff0c;并進行必要的優化和調試。 對于像Firef…

git命令含有中文,終端輸出中文亂碼的問題

目錄 1、[當前代碼頁] 的936 (ANSI/OEM - 簡體中文 GBK) 是導致中文亂碼的原因 2、這樣會導致什么問題呢&#xff1f; (1) 問題一: 【屬性】選項的【字體】無法識別自定義文字樣式&#xff0c;【默認值】選項可選自定義字體樣式&#xff0c;卻無法覆蓋【屬性】選項 (2) 問題…

品牌推廣怎么樣?掌握正確做法,讓品牌大放異彩!

品牌推廣對于初創公司來說是一項至關重要的任務。在市場眾多品牌中&#xff0c;如何脫穎而出&#xff0c;是每個品牌方都要考慮的問題。 作為一名手工酸奶品牌的創始人&#xff0c;目前全國復制了100多家門店&#xff0c;我來分享下&#xff0c;如何推廣&#xff0c;可以讓品牌…