53語言模型和數據集
1.自然語言統計
引入庫和讀取數據:
import random
import torch
from d2l import torch as d2l
import liliPytorch as lp
import numpy as np
import matplotlib.pyplot as plttokens = lp.tokenize(lp.read_time_machine())
一元語法:
# 一元語法
# 因為每個文本行不一定是一個句子或一個段落,因此我們把所有文本行拼接到一起
corpus = [token for line in tokens for token in line]
vocab = lp.Vocab(corpus)
# print(vocab.token_freqs[:5])
# [('the', 2261), ('i', 1267), ('and', 1245), ('of', 1155), ('a', 816)]
freqs = [freq for token, freq in vocab.token_freqs]
d2l.plot(freqs, xlabel='token: x', ylabel='frequency: n(x)',xscale='log', yscale='log')
plt.show()
二元語法:
# 二元語法
bigram_tokens = [pair for pair in zip(corpus[:-1], corpus[1:])]
bigram_vocab = lp.Vocab(bigram_tokens)
# print(bigram_vocab.token_freqs[:5])
# [(('of', 'the'), 309), (('in', 'the'), 169), (('i', 'had'), 130),
# (('i', 'was'), 112), (('and', 'the'), 109)]
freqs = [freq for token, freq in bigram_vocab.token_freqs]
d2l.plot(freqs, xlabel='token: x', ylabel='frequency: n(x)',xscale='log', yscale='log')
plt.show()
三元語法:
# 三元語法
trigram_tokens = [triple for triple in zip(corpus[:-2], corpus[1:-1], corpus[2:])]
trigram_vocab = lp.Vocab(trigram_tokens)
# print(trigram_vocab.token_freqs[:5])
# [(('the', 'time', 'traveller'), 59), (('the', 'time', 'machine'), 30), (('the', 'medical', 'man'), 24),
# (('it', 'seemed', 'to'), 16), (('it', 'was', 'a'), 15)]
freqs = [freq for token, freq in trigram_vocab.token_freqs]
d2l.plot(freqs, xlabel='token: x', ylabel='frequency: n(x)',xscale='log', yscale='log')
plt.show()
對比:
# 一元語法、二元語法和三元語法對比
freqs = [freq for token, freq in vocab.token_freqs]
bigram_freqs = [freq for token, freq in bigram_vocab.token_freqs]
trigram_freqs = [freq for token, freq in trigram_vocab.token_freqs]
d2l.plot([freqs, bigram_freqs, trigram_freqs], xlabel='token: x',ylabel='frequency: n(x)', xscale='log', yscale='log',legend=['unigram', 'bigram', 'trigram'])
plt.show()
2.讀取長序列數據
# n元語法,n 等于 num_steps
# 讀取長序列數據
# 隨機采樣
def seq_data_iter_random(corpus, batch_size, num_steps): #@save"""使用隨機抽樣生成一個小批量子序列"""# 從隨機偏移量開始對序列進行分區,隨機范圍包括num_steps-1# 從一個隨機位置開始截取corpus,以生成一個新的子列表# random.randint(a, b) 會生成一個范圍在 a 到 b 之間的整數,并且包括 a 和 bcorpus = corpus[random.randint(0, num_steps - 1) : ]# 減去1,是因為我們需要考慮標簽num_subseqs = (len(corpus) - 1) // num_steps# 長度為num_steps的子序列的起始索引initial_indices = list(range(0, num_subseqs * num_steps, num_steps))# 在隨機抽樣的迭代過程中,# 來自兩個相鄰的、隨機的、小批量中的子序列不一定在原始序列上相鄰random.shuffle(initial_indices)def data(pos):# 返回從pos位置開始的長度為num_steps的序列return corpus[pos: pos + num_steps]num_batches = num_subseqs // batch_sizefor i in range(0, batch_size * num_batches, batch_size):# 在這里,initial_indices包含子序列的隨機起始索引initial_indices_per_batch = initial_indices[i: i + batch_size]X = [data(j) for j in initial_indices_per_batch]Y = [data(j + 1) for j in initial_indices_per_batch]yield np.array(X), np.array(Y)my_seq = list(range(35))
# for X, Y in seq_data_iter_random(my_seq, batch_size=3, num_steps=5):
# print('X: ', X, '\nY:', Y)
"""
X: [[14 15 16 17 18][19 20 21 22 23][ 9 10 11 12 13]]
Y: [[15 16 17 18 19][20 21 22 23 24][10 11 12 13 14]]
X: [[24 25 26 27 28][29 30 31 32 33][ 4 5 6 7 8]]
Y: [[25 26 27 28 29][30 31 32 33 34][ 5 6 7 8 9]]
"""# 順序分區
def seq_data_iter_sequential(corpus, batch_size, num_steps): #@save"""使用順序分區生成一個小批量子序列"""# 從隨機偏移量開始劃分序列# random.randint(a, b) 會生成一個范圍在 a 到 b 之間的整數,并且包括 a 和 boffset = random.randint(0, num_steps-1)# 根據偏移量和批量大小計算出可以使用的令牌數量,確保所有批次中的樣本數量一致num_tokens = ((len(corpus) - offset - 1) // batch_size) * batch_sizeXs = np.array(corpus[offset: offset + num_tokens]) # 數組Ys = np.array(corpus[offset + 1: offset + 1 + num_tokens])Xs, Ys = Xs.reshape(batch_size, -1), Ys.reshape(batch_size, -1)# print(Xs)# [[ 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18]# [19 20 21 22 23 24 25 26 27 28 29 30 31 32 33]]num_batches = Xs.shape[1] // num_stepsfor i in range(0, num_steps * num_batches, num_steps):X = Xs[:, i: i + num_steps]Y = Ys[:, i: i + num_steps]yield X, Y# for X, Y in seq_data_iter_sequential(my_seq, batch_size=2, num_steps=5):
# print('X: ', X, '\nY:', Y)
"""
X: [[ 4 5 6 7 8][19 20 21 22 23]]
Y: [[ 5 6 7 8 9][20 21 22 23 24]]
X: [[ 9 10 11 12 13][24 25 26 27 28]]
Y: [[10 11 12 13 14][25 26 27 28 29]]
X: [[14 15 16 17 18][29 30 31 32 33]]
Y: [[15 16 17 18 19][30 31 32 33 34]]
"""# 將上面的兩個采樣函數包裝到一個類中, 以便稍后可以將其用作數據迭代器。
class SeqDataLoader: #@save"""加載序列數據的迭代器"""def __init__(self, batch_size, num_steps, use_random_iter, max_tokens):if use_random_iter:self.data_iter_fn = seq_data_iter_randomelse:self.data_iter_fn = seq_data_iter_sequentialself.corpus, self.vocab = lp.load_corpus_time_machine(max_tokens)self.batch_size, self.num_steps = batch_size, num_stepsdef __iter__(self):return self.data_iter_fn(self.corpus, self.batch_size, self.num_steps)def load_data_time_machine(batch_size, num_steps, #@saveuse_random_iter=False, max_tokens=10000):"""返回時光機器數據集的迭代器和詞表"""data_iter = SeqDataLoader(batch_size, num_steps, use_random_iter, max_tokens)return data_iter, data_iter.vocab