循环神经网络和自然语言处理介绍

目标

  • 1.了解tokentokenization
  • 2.知道N-gram的概念和作用。
  • 3.知道文本向量化表示的方法。

1.文本的tokenization

1.1 基本概念

tokenization就是通常所说的分词,分出的每一个词语称为token

1.2 中英文分词的方法

  • 把句子转化为词语
  • 把句子转化为单个字

2. N-gram表示方法

2.1 基本概念

N-gram模型是一种语言模型(Language Model,LM),语言模型是一个基于概率的判别模型,它的输入是一句话(单词的顺序序列),输出是这句话的概率,即这些单词的联合概率(joint probability)。

2.2 N-gram用途

N-gram用途有下:

  • 1.词性标注
  • 2.垃圾短信分类
  • 3.分词器
  • 4.机器翻译和语音识别

3. 向量化

把文本转化成向量有两种方法:

  • 1.转化为one-hot编码
  • 2.转化为word embedding

3.1 one-hot编码

one-hot编码使用稀疏的向量表示文本,占用空间多

3.2 word embedding

word embedding使用了浮点型的稠密矩阵来表示token,根据词典的大小,我们的向量通常使用不同的维度,例如100,256,300等等。其中向量中的每一个值都是超参数,其初始值都是随机生成的,之后会在训练的过程中进行学习而获得。
我们会把所有的文本转化为向量,把句子用向量来表示。
但是在这中间,我们会先把token使用数字来表示,再把数字使用向量来表示。

3.3 word embedding API

使用torch.nn.Embedding(num_embedding,embedding_dim)
参数介绍:

  • 1.num_embedding:词典的大小
  • 2.embedding_dim:embedding的维度
    使用方法:
embedding=nn.Embedding(vocab_size,300)#实例化
input_embeded=embedding(input)#进行embedding的操作

形状的变化:从[batch_size,seq_len]变成[batch_size,seq_len,embedding_dim]

4. 文本情感分类

4.1 目标

  • 1.知道文本处理的基本方法
  • 2.能够使用数据实现情感分类

4.2 思路分析

首先可以把上述问题定义为分类问题,情感评分为1-10。依据之前的学习内容,大致流程如下:

  • 1.准备数据集
  • 2.构建模型
  • 3.模型训练
  • 4.模型评估

4.3 准备数据集

在这一部分,我们需要实例化dataset,准备dataloader。其中需要注意:

  • 1.如何完成基础的Dataset和Dataloader的构建;
  • 2.每个batch中文本的长度不一致的问题如何解决;
  • 3.每个batch中文本如何转化为数字序列。

4.4 文本序列化

实现文本序列化考虑的问题:

  • 1.如何使用字典把词语和数字进行对应
  • 2.不同的词语出现的次数不尽相同,是否需要对高频或者低频词语进行过滤,以及总的词语数量是否需要进行限制
  • 3.得到词典之后,如何把数字序列转化为句子
  • 4.不同句子长度不同,每个batch的句子如何构造出相同的长度(可以对短句子填充特殊字符)
  • 5.对于在测试集中新出现的词语,可以用特殊字符代理
    思路分析:
  • 1.对所有句子进行分词
  • 2.词语存入字典,根据次数对词语进行过滤,并统计次数
  • 3.实现文本转数字序列的方法
  • 4.实现数字序列转文本方法

实现如下:

  • 1.dataset.py:
from torch.utils.data import DataLoader,Dataset
from lib import ws,max_len
import torch
import os
import re

#利用正则方法分词,并且去除不必要的符号
def tokenlize(content):
    re.sub("<.*?>"," ",content)
    fileters=['\.','\t','\n','\x96','\x97']
    content=re.sub("|".join(fileters)," ",content)
    tokens=[i.strip() for i in content.spilt()]
    return tokens


class ImbDataset(Dataset):
    def __init__(self,train=True):
        self.train_data_path=r"..."#训练集数据读取路径
        self.test_data_path = r"..."  #测试集数据读取路径
        data_path=self.train_data_path if train else self.test_data_path

        #把所有文件名放入列表,即pos和neg两个文件夹存入列表
        temp_data_path=[os.path.join(data_path,"pos"),os.path.join(data_path,"neg")]
        self.total_file_path=[]#所有的评论文件的路径path
        for path in temp_data_path:
            file_name_list=os.listdir(path)#得到pos或者neg文件夹内部所有文件名
            file_path_list=[os.path.join(path,i) for i in file_name_list if i.endswith(".txt")]#过滤不是以.txt结尾的文件
            self.total_file_path.extend(file_path_list)


    def __getitem__(self, index):
        file_path=self.total_file_path[index]
        #获取label
        label_str=file_path.split("\\")[-2]
        label=0 if label_str =="neg" else 1
        #获取内容
        tokens=tokenlize(open(file_path).read())
        return tokens,label

    def __len__(self):
        return len(self.total_file_path)

def collate_fn(batch):
    """
    :param batch: ([tokens,label],[tokens,label],...)
    :return:
    """
    content,label=list(zip(*batch))
    content=[ws.transform(i,max_len=max_len) for i in content]
    content=torch.LongTensor(content)#转化成LongTensor,否则在model.py中的embedding()中无法执行,原因是embedding的对象必须是LongTensor
    label=torch.LongTensor(label)
    return content,label

def get_dataloader(train=True):
    imdb_dataset=ImbDataset()
    data_loader=DataLoader(imdb_dataset,batch_size=2,shuffle=True,collate_fn=collate_fn)
    return data_loader
  • 2.word2sequence.py:
import numpy as np
import os

class word2sequence():
    UNK_TAG="UNK"
    PAD_TAG="PAD"

    UNK=0
    PAD=1

    def __init__(self):
        #字典,初始情况下存入两个特殊字符
        self.__dict__={
            self.UNK_TAG:self.UNK,
            self.PAD_TAG:self.PAD
        }
        self.fited=False
        self.count={}#统计词频


    def fit(self,sentence):
        """把单个句子保存到dict中
        :param sentence: [word1,word2,word3...]
        :return:
        """
        for word in sentence:
            self.count[word]=self.count.get(word,0)+1#统计词频

    def bulid_vocab(self,min=0,max=None,max_features=None):
        """
        生成词典
        :param min:最小出现的次数
        :param max:最大的次数
        :param max_features:保留的词语数
        :return:
        """
        # 删除count中词频小于min的单词
        if min is not None:
            self.count={word:value for word,value in self.count if value>=min}
        # 删除count中词频大于max的单词
        if max is not None:
            self.count={word:value for word,value in self.count if value<max}
        #限制保留的词语数
        if max_features is not None:
            temp=sorted(self.count.items(),key=lambda x:x[-1],reverse=True)[:max_features]#排序,取前max_features个词频的单词
            self.count=dict(temp)#sorted之后,结果为列表,需要重新转换成字典

        for word in self.count:
            self.dict[word]=len(self.dict)#给每个单词进行赋值,由于初始情况已经有两个特殊字符,所以新进入的第一个单词的值为2,此后不断叠加

        #得到一个翻转的dict字典(利用键和值的重新匹配)
        self.inverse_dict=dict(zip(self.dict.values(),self.dict.keys()))

    def transform(self,sentence,max_len=None):
        """
        把句子转化成序列
        :param sentence:[wword1,word2,word3...]
        :param max_len: int 对句子进行填充或者裁剪
        :return:
        """
        if max_len is not None:
            if max_len>len(sentence):#填充
                sentence=sentence+[self.PAD_TAG]*(max_len-len(sentence))
            if max_len<len(sentence):#裁剪
                sentence=sentence[:max_len]
        return [self.dict.get(word,self.UNK) for word in sentence]

    def inverse_transform(self,indices):
        """
        把序列转化成句子
        :param indices: [1,2,3...]
        :return:
        """
        return [self.dinverse_dict.get(idx) for idx in indices

from main import word2sequence
import pickle
from dataset import tokenlize #从一开始定义的数据集中导入tokenlize
from tqdm import tqdm#显示可迭代对象的加载进度

if __name__=='__main__':
    ws=word2sequence()
    path=r"..."#写入路径
    temp_data_path=[os.path.join(path,"pos"),os.path.join(path,"neg")]
    for data_path in temp_data_path:
        file_paths=[os,path.join(data_path,file_name) for file_name in os.listdir(data_path)]
        for file_path in tqdm(file_paths):
            sentence = tokenlize(open(file_path).read())
            ws.fit(sentence)
    ws.bulid_vocab(min=10)
    pickle.dump(ws,open("./model/ws.pkl","wb"))#保存文件

  • 3.lib.py:
import pickle
ws=pickle.load(open("./model/ws/pkl","rb"))#保存数据
max_len=20

4.5 构建模型

这里我们只联系使用word embedding,所以模型只有一层,即:

  • 1.数据经过word embedding
  • 2.数据通过全连接层返回结果,计算log_softmax
    为了方便理解,我们只使用了一层模型,所以效果可能并不是特别理想。
    model.py代码如下:
import torch
import torch.nn as nn
from lib import ws,max_len
import torch.nn.functional as F

class MyModel(nn.Module):
    def __init__(self):
        super(MyModel,self).__init__()
        self.embedding=nn.Embedding(len(ws),100)#两个参数,前一个是训练的词语的数量,后一个是每一个词语的维度
        self.fc=nn.Linear(max_len*100,2)#Linear()函数对象必须是二维,所以在forward里面必须进行view()操作

    def forward(self,input):
        """
        :param input: [batch_size,max_len]
        :return:
        """
        x=self.embedding(input)#进行embedding操作,形状成为:[batch_size,max_len,100]
        x=x.view([-1,max_len*100])
        out=self.fc(x)
        return F.log_softmax(out,dim=-1)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值