在介绍word embedding的时候,我们说过,不会直接把文本转化为向量,而是先转化为数字,再把数字转化为向量,那么这个过程如何实现?

这里我们可以考虑把文本中的每个词语和其对应的数字,使用字典保存,同时实现方法把句子通过字典映射为包含数字的列表。

实现文本序列化之前,考虑一下几点:

1.如何使用字典把词语和数字进行对应

2.不同的词语出现的次数不尽相同,是否需要对高频或者低频词语进行过滤,以及总的词语数量是否需要进行限制

3.得到词典之后,如何把句子转化为数字序列,如何把数字序列转化为句子

4.不同句子长度不同,每个batch的句子如何构造成相同的长度(可以对句子进行填充,填充特殊字符)

5.对于新出现的词语在词典中没有出现怎么办(可以使用特殊字符代替)

思路分析:

1.对所有的句子进行分词

2.词语存入字典,根据次数对词语进行过滤,并统计次数

3.实现文本转数字序列的方法

4.实现数字序列转文本的方法

 



解决办法:

不同的词语出现的次数不尽相同,是否需要对高频或者低频词语进行过滤,以及总的词语数量是否需要进行限制

    def build_vocab(self, min=5, 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}
        # 删除大于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]
            self.count = dict(temp)

        for word in self.count:
            self.dict[word] = len(self.dict)

        # 得到一个翻转的dict字典
        self.inverse_dict = dict(zip(self.dict.values(), self.dict.keys()))  # 分别把值和键取出来 zip

把句子构造成相同长度的句子 (填充和裁剪)

    def transform(self, sentence, max_len=None):
        """
        把句子转化成序列
        :param sentence:【word1, word2】
        :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]

完整代码

import numpy as np

class Word2Sequence():
    UNK_TAG = "UNK"  # 特殊字符用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

    def to_index(self, word):
        """word - index"""
        assert self.fited == True, "必须先进行fit操作"
        return self.dict.get(word, self.UNK)
    
    def  to_word(self, index):
        """index - word"""
        assert self.fited,
        if index in self.inversed_dict:
            return self.inversed_dict[index]
        return self.UNK_TAG

    def __len__(self):
        return self(self.dict)

    def fit(self,sentence,min_count=1,max_count=None,max_features=None):    #                   【min_count:最小词频; max_count: 最大词频; max_features: 最大词语数(词典容量大小)】
        """
        :param sentence:[word1,word2,word3]
        :param min_count: 最小出现的次数
        :param max_count: 最大出现的次数
        :param max_feature: 总词语的最大数量
        :return:
        """
        count = {}
        for sentence in sentences:
            for a in sentence:
                if a not in count:
                    count[a] = 0
                count[a] += 1

        if min_count is not None:   # 根据条件统计词频
            count = {k:v for k, v in count.items() if v >= min_count}   
        if max_count is not None:
            count = {k:v for k, v in count.items() if v<= max_count}
        
# 限制最大的数量
        if isinstance(max_feature, int):
            count = sorted(list(count.items()), key=lambda x: x[1])
            if max_feature is not None and len(count) > max_feature:
                count = count[-int(max_feature):]
            for w, _in count:
                self.dict[w] = len(self.dict)
        else:
            for w in sorted(count.keys())
                self.dict[w] = len(self.dict) 
        
        self.fited = True
        # 准备一个index-word的字典
        self.inversed_dict = dict(zip(self.dict.values(), self.dict.keys()))

    def transform(self, sentence, max_len=None):
        """
        实现把句子转化成数组(向量)
        """
        assert self.fited, "必须先进行fit操作"
        if max_len is not None:
            r = [self.PAD]*max_len
        else: 
            r = [self.PAD]*len(sentence)
        if max_len is not None and len(sentence)>max_len:
            sentence = sentence[:max_len]
        for index, word in enumerate(sentence):
            r[index] = self.to_index(word)
        return np.array(r, dtype=np.int64)

    def inverse_transform(self, indices):
        """
        把数组转化成文字
        """
        sentence = []
        for i in indices:
            word = self.to_word(i)
            sentence.append(word)
        return sentence

if __name__ == "__main__"
    w2s = Word2Sequence()
    w2s.fit([
["你", "好", "么"], ["你", "好", "哦"]])
    print(w2s.dict)
    print(w2s.fited)
    print(w2s.transform(["你", "好", "嘛"]))


        

保存 word_sequence 

from word_sequence import Word2Sequence
import os
import pickle
from dataset import tokenize
from tqdm import tqdm

# def a():
#     a = __name__


if __name__ == '__main__':
    a = __name__
    ws = Word2Sequence()
    path = "./data/train"
    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 = tokenize(open(file_path).read())
            ws.fit(sentence)

    ws.build_vocab(min_count=10, max_features=10000)
    pickle.dump(ws, open("./model/ws.pkl", "wb"))  # 把ws保存在本地
    print(len(ws))

注意if __name__=='__main__'的用法!!! 

更多推荐