塞进裤子ヾ(≧O≦)〃嗷~

0%

PyTorch官方教程1-基于逻辑回归和BOW的文本分类器

主要内容

BOW:

不考虑句子中单词的顺序,只考虑词表(vocabulary)中单词在这个句子中的出现次数。

用这句话中单词出现的次数作为向量,向量大小是vocab_size

详情见:http://sbaban.com/NLP%E5%9F%BA%E7%A1%80.html

逻辑回归: sigmoid

讲道理,逻辑回归分两类,网络的输出是一维就行了,然后用sigmoid去和0.5比较。

但官方教程这里分两类用的是softmax,题目是逻辑回归,不知道什么原因。

网络的输出是

$$logsigmoid(Ax+b)$$

采用的损失函数是交叉熵损失函数,不过用的是logsoftmax和NLLLoss()的组合。

1
2
3
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

torch.manual_seed(666)

# 训练集
data = [("me gusta comer en la cafeteria".split(), "SPANISH"),
("Give it to me".split(), "ENGLISH"),
("No creo que sea una buena idea".split(), "SPANISH"),
("No it is not a good idea to get lost at sea".split(), "ENGLISH")]
#测试集
test_data = [("Yo creo que si".split(), "SPANISH"),
("it is lost on me".split(), "ENGLISH")]

word_to_id = {}
label_to_id = {"SPANISH":0,"ENGLISH":1}
for sentence, _ in data + test_data: #['Give', 'it', 'to', 'me'] | ENGLISH
for word in sentence:
if word not in word_to_id:
word_to_id[word] = len(word_to_id)


VOCAB_SIZE = len(word_to_id)
NUM_LABELS = 2 #网络最后的输出尺寸
#2是因为由两个类别

class BoWClassifiler(nn.Module):
def __init__(self, num_labels, vocab_size):
# nn.Module的子类函数必须在构造函数中执行父类的构造函数
super(BoWClassifiler, self).__init__()
# Ax+b
self.linear = nn.Linear(vocab_size, num_labels)
#输入大小是VOCABSIZE,
#因为句子x的BOW表示是词汇表中的单词在这个句子中出现的次数count(word)

def forward(self, bow_vec):
#bow_vec:x的BOW表示
t1 = self.linear(bow_vec) #【样本数,num_labels】
out = F.log_softmax(t1,dim =1)
return out

def make_BoW_vector(sentence, word_to_id):
"""
:param sentence: 输入的一个句子,由word列表构成
:return: 返回输入句子的BOW表示
"""
vec = torch.zeros(VOCAB_SIZE)
for word in sentence:
vec[word_to_id[word]] += 1
return vec.view(1,-1) #view成【1,VOCABSIZE】
#TODO:这里why要view?view成矩阵用于nn.linear和权重相乘,同时第一维表示样本数
#因为采用单是SGD,所以只有一个样本

def make_target(label,label_to_id):
#样本x所对应的真实label的索引
return torch.LongTensor([label_to_id[label]]) #注意有个[]


model = BoWClassifiler(NUM_LABELS, VOCAB_SIZE)
loss_function = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr =0.1)


def train():
#使用SGD训练
model.train()
for epoch in range(50):
for sentence,label in data:
model.zero_grad()
bow_vec = make_BoW_vector(sentence, word_to_id)
target = make_target(label,label_to_id)
log_probs = model(bow_vec)
loss = loss_function(log_probs, target)
loss.backward()
optimizer.step()
print(epoch,loss)

def test():
id_to_label = {v:k for k, v in label_to_id.items()}

model.eval()
with torch.no_grad():
for sentence,label in test_data:
bow_vec = make_BoW_vector(sentence, word_to_id)
target = make_target(label, label_to_id)
log_probs = model(bow_vec)
#选中logprobs最大的为预测的结果
print(id_to_label[log_probs.argmax().item()])

train()
test()


=======
。。。
49 tensor(0.0089, grad_fn=<NllLossBackward>)
SPANISH
ENGLISH
if help:小手一抖点个广告 or 大手一挥资助一下