專門做男士用品的網(wǎng)站長沙企業(yè)seo優(yōu)化
先上代碼:
import numpy as np
import settingsdef generate_random_poetry(tokenizer, model, s=''):"""隨機(jī)生成一首詩:param tokenizer: 分詞器:param model: 用于生成古詩的模型:param s: 用于生成古詩的起始字符串,默認(rèn)為空串:return: 一個字符串,表示一首古詩"""# 將初始字符串轉(zhuǎn)成tokentoken_ids = tokenizer.encode(s)# 去掉結(jié)束標(biāo)記[SEP]token_ids = token_ids[:-1]while len(token_ids) < settings.MAX_LEN:# 進(jìn)行預(yù)測,只保留第一個樣例(我們輸入的樣例數(shù)只有1)的、最后一個token的預(yù)測的、不包含[PAD][UNK][CLS]的概率分布o(jì)utput = model(np.array([token_ids, ], dtype=np.int32))_probas = output.numpy()[0, -1, 3:]del output# print(_probas)# 按照出現(xiàn)概率,對所有token倒序排列p_args = _probas.argsort()[::-1][:100]# 排列后的概率順序p = _probas[p_args]# 先對概率歸一p = p / sum(p)# 再按照預(yù)測出的概率,隨機(jī)選擇一個詞作為預(yù)測結(jié)果target_index = np.random.choice(len(p), p=p)target = p_args[target_index] + 3# 保存token_ids.append(target)if target == 3:breakreturn tokenizer.decode(token_ids)def generate_acrostic(tokenizer, model, head):"""隨機(jī)生成一首藏頭詩:param tokenizer: 分詞器:param model: 用于生成古詩的模型:param head: 藏頭詩的頭:return: 一個字符串,表示一首古詩"""# 使用空串初始化token_ids,加入[CLS]token_ids = tokenizer.encode('')token_ids = token_ids[:-1]# 標(biāo)點(diǎn)符號,這里簡單的只把逗號和句號作為標(biāo)點(diǎn)punctuations = [',', '。']punctuation_ids = {tokenizer.token_to_id(token) for token in punctuations}# 緩存生成的詩的listpoetry = []# 對于藏頭詩中的每一個字,都生成一個短句for ch in head:# 先記錄下這個字poetry.append(ch)# 將藏頭詩的字符轉(zhuǎn)成token idtoken_id = tokenizer.token_to_id(ch)# 加入到列表中去token_ids.append(token_id)# 開始生成一個短句while True:# 進(jìn)行預(yù)測,只保留第一個樣例(我們輸入的樣例數(shù)只有1)的、最后一個token的預(yù)測的、不包含[PAD][UNK][CLS]的概率分布o(jì)utput = model(np.array([token_ids, ], dtype=np.int32))_probas = output.numpy()[0, -1, 3:]del output# 按照出現(xiàn)概率,對所有token倒序排列p_args = _probas.argsort()[::-1][:100]# 排列后的概率順序p = _probas[p_args]# 先對概率歸一p = p / sum(p)# 再按照預(yù)測出的概率,隨機(jī)選擇一個詞作為預(yù)測結(jié)果target_index = np.random.choice(len(p), p=p)target = p_args[target_index] + 3# 保存token_ids.append(target)# 只有不是特殊字符時,才保存到poetry里面去if target > 3:poetry.append(tokenizer.id_to_token(target))if target in punctuation_ids:breakreturn ''.join(poetry)
我們首先看第一個函數(shù)generate_random_poetry,用于隨機(jī)生成古詩。
def generate_random_poetry(tokenizer, model, s=''):"""隨機(jī)生成一首詩:param tokenizer: 分詞器:param model: 用于生成古詩的模型:param s: 用于生成古詩的起始字符串,默認(rèn)為空串:return: 一個字符串,表示一首古詩"""
?tokenizer是一個分詞器,用于將文本轉(zhuǎn)化為模型可以理解的token序列。
# 將初始字符串轉(zhuǎn)成tokentoken_ids = tokenizer.encode(s)# 去掉結(jié)束標(biāo)記[SEP]token_ids = token_ids[:-1]
是將我們傳入的字符串轉(zhuǎn)化為id序列。并使用切片操作切除token序列的最后一個元素。這個元素是用來標(biāo)記結(jié)束標(biāo)志[SEP]的。
我們接著看:
while len(token_ids) < settings.MAX_LEN:# 進(jìn)行預(yù)測,只保留第一個樣例(我們輸入的樣例數(shù)只有1)的、最后一個token的預(yù)測的、不包含[PAD][UNK][CLS]的概率分布o(jì)utput = model(np.array([token_ids, ], dtype=np.int32))_probas = output.numpy()[0, -1, 3:]del output
這里我們就可以看出為什么我們要對最后一個?token進(jìn)行切片處理了,是為了控制預(yù)測的長度。
之后我們進(jìn)行預(yù)測,通過model模型對當(dāng)前的token_ids序列進(jìn)行預(yù)測,model接受一個形狀為(batch_size,sequence_length)的輸入,這里batch_size為1,sequence_length為token_ids的長度。因此,我們將token_ids包裝成一個形狀為(1,sequence_length)的numpy數(shù)組,并將其傳遞給model進(jìn)行預(yù)測。
輸出的結(jié)果是一個3D數(shù)組,形狀為(1,sequence_length,vocab_size)。其中vocab_size是詞匯表,得到的是對下一個詞的預(yù)測。
然后通過output.numpy()將輸出轉(zhuǎn)化為numpy數(shù)組,由于我們只關(guān)注當(dāng)前序列的最后一個token的預(yù)測結(jié)果,所以使用[0,-1,3:]來獲取這部分概率分布。具體的,[0,-1]表示獲取第一個樣例的最后一個token的預(yù)測,而[3:]表示去除前三個token,即[PAD],[UNK]和[CLS]。
最后,通過del output釋放output變量所占用的內(nèi)存空間,這樣做是為了及時釋放內(nèi)存,避免內(nèi)存占用過多。
解釋一下四個特殊的token在自然語言處理任務(wù)中的不同作用。
- [PAD](填充):在序列中用于填充長度不足的部分,使得序列長度統(tǒng)一,在很多深度學(xué)習(xí)模型中,要求輸入的序列長度是相同的,因此當(dāng)序列長度不足時,可以使用[PAD]進(jìn)行填充,使得序列達(dá)到統(tǒng)一的長度。
- [UNK](未知):用于表示模型未見過的或者無法識別的詞匯,當(dāng)模型在處理文本時遇到不在詞匯表中的詞匯,就會用[UNK]表示,這樣可以避免模型對于未知詞匯的處理錯誤。
- [CLS](分類):在自然語言處理中的一些任務(wù)中,例如文本分類,文本語義相似度等,[CLS]被用作特殊的起始標(biāo)記。模型會將[CLS]作為整個序列的表示,并用于后續(xù)任務(wù)的處理。例如:對于文本分類任務(wù),模型可以根據(jù)[CLS]表示進(jìn)行預(yù)測分類標(biāo)簽。
- [SEP](分隔符):用于表示序列的分隔,常見于處理多個句子或多個文本之間的任務(wù)。他可以用來分隔句子,句子對,文本片段等,在一些任務(wù)中,如文本對分類,問答系統(tǒng)等,輸入可能包含多個句子或文本片段,模型可以通過識別[SEP]來理解不同句子之間的關(guān)系。另外,[SEP]還可以用于標(biāo)記序列的結(jié)束,在一些 情況下,序列的長度是固定的,使用[SEP]標(biāo)記還可以表示序列的結(jié)束。
繼續(xù)看我們的代碼:
# 按照出現(xiàn)概率,對所有token倒序排列p_args = _probas.argsort()[::-1][:100]# 排列后的概率順序p = _probas[p_args]# 先對概率歸一p = p / sum(p)# 再按照預(yù)測出的概率,隨機(jī)選擇一個詞作為預(yù)測結(jié)果target_index = np.random.choice(len(p), p=p)target = p_args[target_index] + 3# 保存token_ids.append(target)
?首先,通過argsort()函數(shù)對得到的所有outputs進(jìn)行從小到大的排序,然后經(jīng)過[::-1]將排序結(jié)果進(jìn)行反轉(zhuǎn),這樣就變成了從大到小的排序,[:100]表示只選擇排名前100的token。返回的是_probas中元素從小到大排序的索引數(shù)組。
得到索引數(shù)組之后,我們通過p=_probas[p_args],根據(jù)排名靠前的token的索引p_args,從預(yù)測的概率分布_probas中獲取對應(yīng)的概率值,這樣得到的p就是排列后的概率順序(概率值從大到小)。
之后對概率進(jìn)行歸一化,將概率值除以概率之和,使其概率和為1。
target_index = np.random.choice(len(p), p=p)根據(jù)參數(shù)p(給定的概率分布)在長度為len(p)的范圍內(nèi)進(jìn)行隨機(jī)選擇,并返回選擇的索引target_index。(顯然,元素對應(yīng)的p值越大,被選擇的概率就越高)。
后面的target = p_args[target_index] + 3,其中+3是必不可少的,我剛剛因為沒有+3就運(yùn)行報錯了,因為我們得到的target是對應(yīng)于token_ids的索引,而token_ids的前三項是特殊字符,為了和實際中的模型相對應(yīng),我們需要將預(yù)測的token編號做一些偏離處理。
最后我們將我們的預(yù)測列表中加上我們預(yù)測的詞的索引值,也就是target。
看著一段:
if target == 3:break return tokenizer.decode(token_ids)
如果生成的target的值是3,表示生成的token是特殊的[CLS]標(biāo)記,這意味著生成的序列已結(jié)束。
返回我們解碼之后的結(jié)果也就是生成的詩詞。等于3的情況是存在的,3意味著結(jié)束標(biāo)志SPE,當(dāng)句子的長度合適時,其預(yù)測的下一個詞很可能是3,我們規(guī)定3也屬于可預(yù)測范圍。
看生成藏頭詩的代碼:
def generate_acrostic(tokenizer, model, head):"""隨機(jī)生成一首藏頭詩:param tokenizer: 分詞器:param model: 用于生成古詩的模型:param head: 藏頭詩的頭:return: 一個字符串,表示一首古詩"""# 使用空串初始化token_ids,加入[CLS]token_ids = tokenizer.encode('')token_ids = token_ids[:-1]# 標(biāo)點(diǎn)符號,這里簡單的只把逗號和句號作為標(biāo)點(diǎn)punctuations = [',', '。']punctuation_ids = {tokenizer.token_to_id(token) for token in punctuations}# 緩存生成的詩的listpoetry = []# 對于藏頭詩中的每一個字,都生成一個短句for ch in head:# 先記錄下這個字poetry.append(ch)# 將藏頭詩的字符轉(zhuǎn)成token idtoken_id = tokenizer.token_to_id(ch)# 加入到列表中去token_ids.append(token_id)# 開始生成一個短句while True:# 進(jìn)行預(yù)測,只保留第一個樣例(我們輸入的樣例數(shù)只有1)的、最后一個token的預(yù)測的、不包含[PAD][UNK][CLS]的概率分布o(jì)utput = model(np.array([token_ids, ], dtype=np.int32))_probas = output.numpy()[0, -1, 3:]del output# 按照出現(xiàn)概率,對所有token倒序排列p_args = _probas.argsort()[::-1][:100]# 排列后的概率順序p = _probas[p_args]# 先對概率歸一p = p / sum(p)# 再按照預(yù)測出的概率,隨機(jī)選擇一個詞作為預(yù)測結(jié)果target_index = np.random.choice(len(p), p=p)target = p_args[target_index] + 3# 保存token_ids.append(target)# 只有不是特殊字符時,才保存到poetry里面去if target > 3:poetry.append(tokenizer.id_to_token(target))if target in punctuation_ids:breakreturn ''.join(poetry)
我們看一下tokenizer.id_to_token(3,2,1,0)
這段代碼和上面的隨機(jī)生成古詩類似,不同的是:
上面的是完整生成一首古詩,其中就算遇到“,”或者“?!辈粫袛?#xff0c;直到遇到結(jié)束符號[SEP]才會中止。
而我們生成的藏頭詩是對每一個字進(jìn)行生成,這里遇到“,”,“。”就進(jìn)行下一個字的繼續(xù)生成。然后將每個字的自動生成進(jìn)行拼接。得到完整的詩句。(當(dāng)然,遇到[SEP]也是進(jìn)行終止的,保證了句式的協(xié)調(diào))。
return ''.join(poetry)
作用是將我們的peotry中的內(nèi)容(短句),連接成一個字符串。并將其作為函數(shù)值返回。
如果不加join函數(shù)的話,輸出是一個列表: