Tokenizer & 类处理

NLP—tokenizer,关于类的操作

在这里插入图片描述

文本处理

def filter(path1,path2):
    f1=open(path1,"r",encoding='utf-8')#读入评论
    f2=open(path2,"w",encoding='utf-8')#清洗评论并保存
    lis=f1.readlines()
    for line in lis:
        line=line.strip().split("\t")
        line[1] = re.sub("我在:http:.*", "", line[1])  
        line[1] = re.sub("我在这里:http:.*", "", line[1])
        line[1] = re.sub("http:.*", "", line[1])
        f2.write(line[1]+"\n")
    f1.close()
    f2.close()

读入的文件的格式跟上一次的微博文本评论一样,可以采用类似的清洗方法,但是当时在使用harvesttext函数的时候,并没用帮我把url清洗掉,于是直接使用正则表达式。使用re.sub()做正则表达式的匹配和替换,"我在:http:.*“指的是从我在http:开始匹配到结尾,将其替换为空字符。在清洗的过程中,我发现评论的url前缀有三种情况,为"我在:http:”,"我在这里:http:‘’,“http:”。将每条评论按行写入一个新的文本,同时该文本一行表示一条评论。

图像绘制

def picture_of_distribution(char):
    len_c=[]
    len_w=[]
    for line in char:#提前生成一各数字列表
        x=len(jb.lcut(line.strip(), cut_all=False))
        len_w.append(x)
        y=len(line.strip())
        len_c.append(y)
    plt.figure(figsize=(16,16))
    plt.subplot(2,1,1)
    plt.boxplot(len_w,showmeans=True)
    plt.title("word_split_distribution")
    plt.subplot(2,1,2)
    plt.boxplot(len_c,showmeans=True)
    plt.title("char_split_distribution")
    plt.savefig("D:\\经管大三\\现代程序设计\\week5\\boxplot.png")


    plt.figure(figsize=(16,16))
    plt.subplot(2,1,1)
    sns.distplot(len_w)#这个distplot函数巨坑,要不是为了拟合曲线真不想用
    plt.title("word_split_distribution")
    plt.subplot(2,1,2)
    sns.distplot(len_c)
    plt.title("char_split_distribution")
    plt.savefig("./hist_plot.png")
    plt.show()

该函数传入了一个列表,每个元素指一条评论,以"\n"结尾。

首先,对每条评论通过两种方法截断,一种是按字截断,一种是按照分词截取,统计其长度。

接着将两种方式统计的评论长度绘制箱线图和直方图。但是在使用seaborn直方图的时候,pc报错显示distplot函数将未来将要被弃用,改为displot就可以正常绘制,如果需要添加拟合曲线,设置参数kde=True即可。

箱线图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HMGMyopl-1665400325972)(D:\经管大三\现代程序设计\week5\boxplot.png)]

通过分析箱线图,发现词和字分割方式的中位数在15到25之间。这也比较符合实际情况,大家评论的时候也不太愿意长篇大论的码字,当然不排除有些人喜欢发很多字的评论,观察箱线图即可看出,这些离群值,大大拉升了平均值,使得均值高于中位数很多。

直方图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-edkWqb9Z-1665400325974)(D:\经管大三\现代程序设计\week5\hist_plot_2.png)]

通过在窗口界面自动返回的坐标显示字切割的众数在15左右,词众数在10左右。发现一个很奇特的现象,按字分词到150以后就几乎没有了。但是通过查阅,微博评论字数限制在140,那么为什么会出现这种情况呢?我觉得存粹是因为画直方图的时候,采用了区间的方式,恰好包含了超出150的部分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AA8pJu7F-1665400325975)(D:\经管大三\现代程序设计\week5\char_hist_plot.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6YsTAgF1-1665400325976)(D:\经管大三\现代程序设计\week5\word_hist_plot.png)]

以上采用的是displot函数

通过分析,对于字数分割的评论,选取15到20长度是比较合适的;对于词分割的评论,选取10到15长度是比较合适的。

类的定义

class Token:
    def __init__(self,char,coding="w",PAD=0):#这一步函数是做初始化和对实例进行赋值
        __dic={}#不希望外部访问
        __dic["PAD"]=0
        self.char=char#此时每个元素还是代表一行评论
        self.coding=coding
        i=1
        char_ = [item.strip() for item in char]  # 因为后面是按行读入,而读入类的是需要作为整个字符串做分词的,所以需要删去回车键,做点小操作
        char_ = " ".join(char_)
        if coding=="w":#按词进行编码
            word_lis=jb.lcut(char_,cut_all=False)
            for item in word_lis:
                if item not in __dic.keys():
                    __dic[item]=i
                    i+=1
        elif coding=="c":#按字编码
            for cha in char_:
                if cha in __dic.keys():
                    __dic[cha]=i
                    i+=1
        self.dic=__dic#便于之后类内的函数调用
    def tokenize(self,sentences):
        list_of_chars=[]
        if self.coding=="w":
            list_of_chars=jb.lcut(sentences.strip(), cut_all=False)#先删除末尾行的\n
        elif self.coding=="c":
            for i in sentences.strip():
                list_of_chars.append(i)
        return list_of_chars#返回分词列表

    def encode(self,list_of_chars):
        tokens=[]
        for i in list_of_chars:
            tokens.append(self.dic[i])
        return tokens

    def trim(self,tokens,seq_len):
        while len(tokens)<seq_len:
            tokens.append(0)
        tok=tokens[:seq_len]
        return tok

    def decode(self,tokens):
        #key=list(self.dic.keys())这样子不可行,因为字典不是按顺序排列的,可以利用items返回键值对
        dic=self.dic
        dic=sorted(dic.items(),key=lambda x:x[1])#对字典按照键值排序,同时返回成列表的键值对
        key=[]
        value=[]
        for item in dic:
            x, y = item
            key.append(x)
            value.append(y)#加入键值对
        for item in tokens:#键入的是数字
            print(key[item],end=" ")
        print("\n")

    def encode_all(self,seq_len):
        lis=self.char
        tokens=[]
        for line in lis:
            toke=self.tokenize(line)
            if len(toke)==seq_len:#输出的时候连成一个完整的句子
                toke="".join(toke)
                tokens.append(toke)
                print(toke)
        return(tokens)

main函数

path1="./final_none_duplicate.txt"
path2="./filter_comment.txt"
#filter(path1,path2)
f1=open(path2,"r",encoding="utf-8")
char=f1.readlines()
picture_of_distribution(char)
s=Token(char,coding="w")
list_of_chars=s.tokenize(char[0])
print(char[0])
tokens=s.encode(list_of_chars)
print(tokens)
tok=s.trim(tokens,15)
print(tok)
s.decode(tokens)
all=s.encode_all(15)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0BegVdTc-1665400325978)(C:\Users\kerrla\AppData\Roaming\Typora\typora-user-images\image-20220930172015747.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rAcl4Tul-1665400325979)(D:\经管大三\现代程序设计\week5\长度为15的评论.png)]

与第二周的作业中one-hot方法相比,one-hot将特征词作为向量,然后将每条语料用特征词向量表示。而tokenizer方法则将所有的词都转化为数字,然后将预料表示为向量。区别就在于one-hot可以反映出预料的含义,如果将情绪词作为特征词向量,则能反映语料所表达的情绪,但是one-hot不能反映语料本身;tokenizer可以反映预料本身的结构,但是这种表示方法并不能反映语料的含义,同时tokenizer方法灵活性不够,如果从外部输入一些语料库中没有的词,则无法被表示,同时这种处理方法对数据存储等方面消耗太大。

附加题

(3条消息) 一文读懂BERT(原理篇)_sunhua93的博客-CSDN博客_bert

tokenizer方法灵活性不够,如果从外部输入一些语料库中没有的词,则无法被表示,同时这种处理方法对数据存储等方面消耗太大。

附加题

(3条消息) 一文读懂BERT(原理篇)_sunhua93的博客-CSDN博客_bert

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值