【自然语言处理(NLP)】NLP实战:IMDB影评情感分析项目

个人主页:道友老李
欢迎加入社区:道友老李的学习社区

介绍

**自然语言处理(Natural Language Processing,NLP)**是计算机科学领域与人工智能领域中的一个重要方向。它研究的是人类(自然)语言与计算机之间的交互。NLP的目标是让计算机能够理解、解析、生成人类语言,并且能够以有意义的方式回应和操作这些信息。

NLP的任务可以分为多个层次,包括但不限于:

  1. 词法分析:将文本分解成单词或标记(token),并识别它们的词性(如名词、动词等)。
  2. 句法分析:分析句子结构,理解句子中词语的关系,比如主语、谓语、宾语等。
  3. 语义分析:试图理解句子的实际含义,超越字面意义,捕捉隐含的信息。
  4. 语用分析:考虑上下文和对话背景,理解话语在特定情境下的使用目的。
  5. 情感分析:检测文本中表达的情感倾向,例如正面、负面或中立。
  6. 机器翻译:将一种自然语言转换为另一种自然语言。
  7. 问答系统:构建可以回答用户问题的系统。
  8. 文本摘要:从大量文本中提取关键信息,生成简短的摘要。
  9. 命名实体识别(NER):识别文本中提到的特定实体,如人名、地名、组织名等。
  10. 语音识别:将人类的语音转换为计算机可读的文字格式。

NLP技术的发展依赖于算法的进步、计算能力的提升以及大规模标注数据集的可用性。近年来,深度学习方法,特别是基于神经网络的语言模型,如BERT、GPT系列等,在许多NLP任务上取得了显著的成功。随着技术的进步,NLP正在被应用到越来越多的领域,包括客户服务、智能搜索、内容推荐、医疗健康等。

IMDB影评情感分析

在这里插入图片描述

项目数据集

imdb数据集下载地址: http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz

项目实现

1. 导包

import os
import torch
from torch import nn
import dltools

2. 加载IMDB数据

def read_imdb(data_dir, is_train):
    data, labels = [], []
    for label in ('pos', 'neg'):
        folder_name = os.path.join(data_dir, 'train' if is_train else 'test', label)
        for file in os.listdir(folder_name):
            with open(os.path.join(folder_name, file), 'rb') as f:
                review = f.read().decode('utf-8').replace('\n', '')
                data.append(review)
                labels.append(1 if label == 'pos' else 0)
    return data, labels

data_dir = r'D:\Projects\课程资料\自然语言处理(NLP)\学习\data\aclImdb'
train_data = read_imdb(data_dir, is_train=True)
print('训练集数目: ', len(train_data[0]))
训练集数目:  25000

3. 查看部分数据

for x, y in zip(train_data[0][:3], train_data[1][:3]):
    print('标签: ', y,'review: ', x[0:60])

在这里插入图片描述

4. 分词

train_tokens = dltools.tokenize(train_data[0], token='word')
vocab = dltools.Vocab(train_tokens, min_freq=5, reserved_tokens=['<pad>'])

dltools.set_figsize()
dltools.plt.xlabel('# tokens per review')
dltools.plt.ylabel('count')
dltools.plt.hist([len(line) for line in train_tokens], bins=range(0, 1000, 50))

在这里插入图片描述

5. 加载数据整合

def load_data_imdb(data_dir, batch_size, num_steps=500):
    train_data = read_imdb(data_dir, True)
    test_data = read_imdb(data_dir, False)
    train_tokens = dltools.tokenize(train_data[0], token='word')
    test_tokens = dltools.tokenize(test_data[0], token='word')
    
    vocab = dltools.Vocab(train_tokens, min_freq=5, reserved_tokens=['<pad>'])
    train_features = torch.tensor([dltools.truncate_pad(vocab[line], num_steps, vocab['<pad>']) for line in train_tokens])
    test_features = torch.tensor([dltools.truncate_pad(vocab[line], num_steps, vocab['<pad>']) for line in test_tokens])
    
    train_iter = dltools.load_array((train_features, torch.tensor(train_data[1])), 64)
    test_iter = dltools.load_array((test_features, torch.tensor(train_data[1])), 64)
    return train_iter, test_iter, vocab

data_dir = r'D:\Projects\课程资料\自然语言处理(NLP)\学习\data\aclImdb'
train_iter, test_iter, vocab = load_data_imdb(data_dir, 64, num_steps=500)

在这里插入图片描述

6. 构建模型

前向传播:

  • inputs (batch_size, num_steps)
    • 循环神经网络输入的第一个维度是时间维
    • 所以要对inputs做一个转置, 转置之后的形状(num_steps, batch_size)
    • 再经过embedding层, 就变成(num_steps, batch_size, embed_size)
    • 为了提供内存利用率和效率, 调用flatten_parameters让parameters的数据存放在内存中连续的块中. contiguous
    • 返回两个东西: 输出, 上一个隐藏层在不同时间步的隐状态
    • outputs :(num_steps, batch_size, 2 * num_hiddens)
    • 连接初始和最终时间步的隐藏状态, 做为全连接的输入.
class BiRNN(nn.Module):
    def __init__(self, vocab_size, embed_size, num_hiddens, num_layers, **kwargs):
        super().__init__(**kwargs)
        self.embedding = nn.Embedding(vocab_size, embed_size)
        self.encoder = nn.LSTM(embed_size, num_hiddens, num_layers=num_layers, bidirectional=True)
        self.decoder = nn.Linear(4 * num_hiddens, 2)
        
        
    def forward(self, inputs):
        embedding = self.embedding(inputs.T)
        self.encoder.flatten_parameters()
        outputs, _ = self.encoder(embedding)
        # (batch, 4 * num_hiddens)
        encoding = torch.cat((outputs[0], outputs[-1]), dim=1)
        outs = self.decoder(encoding)
        return outs

7. 词嵌入

class TokenEmbedding:
    def __init__(self, file_path):
        self.idx_to_token, self.idx_to_vec = self._load_embedding(file_path)
        self.unknown_idx = 0
        self.token_to_idx = {token: idx for idx, token in enumerate(self.idx_to_token)}
    
    # 加载预训练词向量
    def _load_embedding(self, file_path):
        # bos eos unk...
        idx_to_token, idx_to_vec = ['<unk>'], []
        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                elems = line.rstrip().split(' ')
                token, elems = elems[0], [float(elem) for elem in elems[1:]]
                # 跳过fasttext的第一行
                if len(elems)> 1:
                    idx_to_token.append(token)
                    idx_to_vec.append(elems)
                    
        idx_to_vec = [[0] * len(idx_to_vec[0])] + idx_to_vec
        return idx_to_token, torch.tensor(idx_to_vec)
    
    def __getitem__(self, tokens):
        indices = [self.token_to_idx.get(token, self.unknown_idx) for token in tokens]
        vecs = self.idx_to_vec[torch.tensor(indices)]
        return vecs
    
    def __len__(self):
        return len(self.idx_to_token)

8. 初始化模型和权重

embed_size, num_hiddens, num_layers = 100, 100, 2
devices = dltools.try_all_gpus()
net = BiRNN(len(vocab), embed_size, num_hiddens, num_layers)

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.xavier_uniform_(m.weight)
    if type(m) == nn.LSTM:
        for param in m._flat_weights_names:
            if 'weight' in param:
                nn.init.xavier_uniform_(m._parameters[param])
                
net.apply(init_weights)
BiRNN(
  (embedding): Embedding(49347, 100)
  (encoder): LSTM(100, 100, num_layers=2, bidirectional=True)
  (decoder): Linear(in_features=400, out_features=2, bias=True)
)

9. glove词向量

glove_embedding = TokenEmbedding(r'D:\Projects\课程资料\自然语言处理(NLP)\学习\data\glove.6B.100d.txt')
embeds = glove_embedding[vocab.idx_to_token]

10. 训练和评估

net.embedding.weight.data.copy_(embeds)
net.embedding.weight.requires_grad = False

# 训练和评估
lr, num_epochs = 0.01, 100
trainer = torch.optim.Adam(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss(reduction='none')
dltools.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)

在这里插入图片描述
在这里插入图片描述

后面的省略…

11. 预测

def predict_sentiment(net, vocab, sequence):
    sequence = torch.tensor(vocab[sequence.split()], device=dltools.try_gpu())
    label = torch.argmax(net(sequence.reshape(1, -1)), dim=1)
    return 'positive' if label == 1 else 'negative'

predict_sentiment(net, vocab, 'this moive is great')
predict_sentiment(net, vocab, 'this moive is so bad')
'positive'
'negative'
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

道友老李

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值