小型中文版聊天机器人

2023-11-18

入门小菜鸟,希望像做笔记记录自己学的东西,也希望能帮助到同样入门的人,更希望大佬们帮忙纠错啦~侵权立删。

目录

一、简单介绍与参考鸣谢

二、数据集介绍

三、数据预处理

1、重复标点符号表达

2、英文标点符号变为中文标点符号

3、繁体字转为简体字

4、限定长度

5、为后面制作词表做准备

6、代码实现(sol_data.py文件)

7、处理完的数据展示

四、词表制作以及转化(word2seq.py文件)

五、数据集加载(dataset.py)

六、GPT模型搭建(gpt_model.py)

1、原理解析

(1)Transformer与GPT

(2)多头注意力机制

 (3)多头注意力机制

2、代码

七、训练模型(train.py)

八、补充实现(utils.py)

九、完整代码与结果


一、简单介绍与参考鸣谢

自己用pytorch搭建模型,训练一个小型的中文闲聊机器人。

数据集和实现思路部分参考这位博主大大@weixin_44599230的博文GPT模型介绍并且使用pytorch实现一个小型GPT中文闲聊系统_tinygpt_weixin_44599230的博客-CSDN博客

这位博主真的 tql 呜呜呜,他后续还出了另外一个版本的闲聊系统,在此鸣谢!


二、数据集介绍

数据集地址百度网盘 提取码jk8d

这份数据集是纯中文,没有英文、颜文字、数字等之类的干扰呜呜呜。

这份数据集是多轮次对话数据集,数据规模为50w,每一轮次用空行隔开。


三、数据预处理

虽然说这份数据集已经很nice了,但还是有一丢丢需要处理

1、重复标点符号表达

        比如说:将“??????????????????”缩成“?”

        emmm其实这个重不重复木有太大关系,主要是我想让他长度短一点,这样少点点计算

2、英文标点符号变为中文标点符号

        比如说:“?"变为”?“

        这也是为了让他别太复杂,算力有限能省就省(穷.jpg)

3、繁体字转为简体字

       使用zhconv这个包。

       这个包的下载安装以及使用方法可以看看这篇python汉字简繁体转换方法_python繁体字转简体字_一位代码的博客-CSDN博客

这位博主写得很清楚,点赞

4、限定长度

       同样的算力有限哈哈哈,并且也希望数据集中样本长度相差别太大。我是限制每个样本(加上每句对话结束符后)长度不能超过100,这样数据集规模变为48w+。

5、为后面制作词表做准备

       每个词(包括每句对话的结束符)用空格分开。

       最早的时候我的想法是用jieba分词,将所得的所有分词作为我的词表,但是分词后词表的词频更加稀疏了,而且词表大小巨大(如果我没记错的话,词表大小是以万为单位的,害怕极了)。

      于是乎我放弃了这种想法,就单独一个字一个字作为词来分就好了,这样词表大小是6000+个,大大减小了词表大小,词频也不会过于稀疏。

6、代码实现(sol_data.py文件)

其中的"import config"是我将所有配置信息都写在了config.py文件中,方便调整。

其中config.max_len=100,即前面提到的限制长度不超过100;

config.data_path_txt,即预处理后的数据保存地址

import re
from tqdm import tqdm
import zhconv
import config

#处理重复符号的表达,如替换多个重复符号
def delete_repeat(s):
    #注释掉的是英文的表达
    #s = re.sub('[!]+','!', s)
    #s = re.sub('[?]+','?', s)
    #s = re.sub('[,]+',',', s)
    #s = re.sub('[:]+',':', s)
    #s = re.sub('[;]+',';', s)
    s = re.sub('[,]+',',', s)
    s = re.sub('[!]+','!', s)
    s = re.sub('[?]+','?', s)
    s = re.sub('[:]+',':', s)
    s = re.sub('[;]+',';', s)
    s = re.sub('[。]+','。', s)
    s = re.sub('[、]+','、', s)
    return s

with open('data/origin_train.txt','r',encoding='utf-8') as f: #打开原始数据集
    lines = f.readlines()

train_datas = []
temp_data = ''
#每个多轮对话中使用'<EOS>'将其划分
for line in tqdm(lines):

    if line!='\n':
        line = line.strip() #去除前导后方空格
        #英文标点符号置换为中文标点符号
        line = line.replace('!','!')
        line = line.replace('?','?')
        line = line.replace(',',',')
        line = line.replace('.','。')
        line = line.replace(':',':')
        line = line.replace(';',';')
        line = zhconv.convert(line, 'zh-cn') #转为简体字
        line = " ".join(line)
        temp_data+=(line+' <EOS> ')
    else:
        if len(temp_data.split()) <= config.max_len: #限制长度
            train_datas.append(temp_data)
        temp_data=''

with open(config.data_path_txt,'w',encoding='utf-8') as f: #将处理后的数据保存在另一个文件中
    for train_data in train_datas:
        f.write(train_data+'\n')

7、处理完的数据展示


四、词表制作以及转化(word2seq.py文件)

       先定义填充符<PAD>,未知符<UNK>和结束符<EOS>,然后再对数据集中的词进行标号,生成词表与转义词表,最后统计数据集中每个词出现的词频,生成一个词频表可以直观看看咱们的词表情况。

       并且定义词到标号,标号到词的转化的方法,方便后期训练以及测试时使用。

其中,config.word_sequence_dict是保存词典的位置

#生成词表
#构造文本序列化和反序列化方法(文本转数字)
import pickle
import config
from tqdm import tqdm

class Word2Sequence():
    PAD_TAG = "<PAD>" #填充编码
    UNK_TAG = "<UNK>" #未知编码
    EOS_TAG = "<EOS>" #句子结尾

    #上面四种情况的对应编号
    PAD = 0
    UNK = 1
    EOS = 2

    def __init__(self):

        #文字——标号字典
        self.dict = {
            self.PAD_TAG :self.PAD,
            self.UNK_TAG :self.UNK,
            self.EOS_TAG :self.EOS
        }
        #词频统计
        self.count = {}
        self.fited = False #是否统计过词典了

    #以下两个转换都不包括'\t'
    #文字转标号(针对单个词)
    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 == True, "必须先进行fit操作"
        if index in self.inversed_dict:
            return self.inversed_dict[index]
        return self.UNK_TAG

    # 获取词典长度
    def __len__(self):
        return len(self.dict)

    #统计词频生成词典
    def fit(self, sentence):
        """
        :param sentence:[word1,word2,word3]
        """
        for a in sentence:
            if a not in self.count:
                self.count[a] = 0
            self.count[a] += 1

        self.fited = True

    def build_vocab(self, min_count=config.min_count, max_count=None, max_feature=None):

        """
        :param min_count: 最小出现的次数
        :param max_count: 最大出现的次数
        :param max_feature: 总词语的最大数量
        """

        # 限定统计词频范围
        if min_count is not None:
            self.count = {k: v for k, v in self.count.items() if v >= min_count}
        if max_count is not None:
            self.count = {k: v for k, v in self.count.items() if v <= max_count}

        # 给对应词进行编号
        if isinstance(max_feature, int): #是否限制词典的词数
            #词频从大到小排序
            count = sorted(list(self.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: #按字典序(方便debug查看)
            for w in sorted(self.count.keys()):
                self.dict[w] = len(self.dict)

        # 准备一个index->word的字典
        self.inversed_dict = dict(zip(self.dict.values(), self.dict.keys()))

        #debug专用
        f_debug_word = open("data/debug_word.txt","w",encoding='utf-8')
        t = 0
        for key,_ in self.dict.items():
            t = t + 1
            if t >= 4: #排除那3种情况(填充,未知,结尾)
                f_debug_word.write(key+"★ "+str(self.count[key]) + "\n") #使用★ 区分是为了防止其中的词语包含分隔符,对我们后续的操作不利

        f_debug_word.close()

    def transform(self, sentence,max_len=None,add_eos=True):
        """
        实现把句子转化为向量
        :param max_len: 限定长度
        :param add_eos: 是否在最后再补上<EOS>结束符
        :return:
        """
        assert self.fited == True, "必须先进行fit操作"

        r = [self.to_index(i) for i in sentence]
        if max_len is not None: #限定长度
            if max_len>len(sentence):
                if add_eos:
                    #添加结束符与填充符达到一定长度
                    r+=[self.EOS]+[self.PAD for _ in range(max_len-len(sentence)-2)]
                else: #添加填充符达到一定长度
                    r += [self.PAD for _ in range(max_len - len(sentence)-1)]
            else:
                if add_eos:
                    r = r[:max_len-2]
                    r += [self.EOS]
                else:
                    r = r[:max_len-1]
        else:
            if add_eos:
                r += [self.EOS]

        return r

    def inverse_transform(self,indices):
        """
        实现从句子向量 转化为 词(文字)
        :param indices: [1,2,3....]
        :return:[word1,word2.....]
        """
        sentence = []
        for i in indices:
            word = self.to_word(i)
            sentence.append(word)
        return sentence

#以下可供第一次运行,下一次就可以注释掉了

#初始
word_sequence = Word2Sequence()
#词语导入
for line in tqdm(open(config.data_path.txt,encoding='utf-8').readlines()):
    word_sequence.fit(line.strip().split())

print("生成词典...")
word_sequence.build_vocab(min_count=None,max_count=None,max_feature=None)
print("词典大小:",len(word_sequence.dict))
pickle.dump(word_sequence,open(config.word_sequence_dict,"wb")) #保存词典

五、数据集加载(dataset.py)

       定义一个ChatDataset类,可以逐一取出数据,并且获取数据集大小。

       并且定义一个处理数据的方法——将句子中的词转为标号,并且进行填充。这里并不是整份数据集都是一样的样本长度,只要保证一个batch里的样本长度一致就好了(不一致就填充),这样设计的原因见后面的模型原理分析。

#构建数据集
import torch
import pickle
import config
from torch.utils.data import Dataset,DataLoader
from tqdm import tqdm
from word2seq import Word2Sequence

word_sequence = pickle.load(open(config.word_sequence_dict,"rb")) #词典加载

class ChatDataset(Dataset):
    def __init__(self):
        super(ChatDataset,self).__init__()

        #读取内容
        data_path = config.data_path_txt
        self.data_lines = open(data_path,encoding='utf-8').readlines()

    #获取对应索引的问答
    def __getitem__(self, index):
        input = self.data_lines[index].strip().split()[:-1]
        target = self.data_lines[index].strip().split()[1:]
        #为空则默认读取下一条
        if len(input) == 0 or len(target)==0:
            input = self.data_lines[index+1].split()[:-1]
            target = self.data_lines[index+1].split()[1:]
        #此处句子的长度如果大于max_len,那么应该返回max_len
        return input,target,len(input),len(target)

    #获取数据长度
    def __len__(self):
        return len(self.data_lines)
    
# 整理数据————数据集处理方法
def collate_fn(batch):

    # 排序
    batch = sorted(batch,key=lambda x:x[2],reverse=True) #输入长度排序
    input, target, input_length, target_length = zip(*batch)

    max_len = max(input_length[0],target_length[0]) #这里只需要固定每个batch里面的样本长度一致就好,并不需要整个数据集的所有样本长度一致

    # 词变成词向量,并进行padding的操作
    input = torch.LongTensor([word_sequence.transform(i, max_len=max_len, add_eos=False) for i in input])
    target = torch.LongTensor([word_sequence.transform(i, max_len=max_len, add_eos=False) for i in target])

    input_length = torch.LongTensor(input_length)
    target_length = torch.LongTensor(target_length)

    return input, target

print("数据集装载...")
data_loader = DataLoader(dataset=ChatDataset(),batch_size=config.batch_size,shuffle=True,collate_fn=collate_fn,drop_last=True)

'''''
#测试专用(debug)
if __name__ == '__main__':
    for idx, (input, target) in enumerate(data_loader):
        print(idx)
        print(input)
        print(target)
'''''

六、GPT模型搭建(gpt_model.py)

1、原理解析

(1)Transformer与GPT

      说到GPT就要提到Transformer啦。GPT是Transformer的Decoder部分。

Transformer的网络结构如下:(图是网上找的,侵权立删)

而GPT则如下:

 因为其没有encoder的输出作为另一个输入分支,所以去掉了encoder-decoder的attention机制。

(2)多头注意力机制

A、提出原因

       self attention是通过某种运算来直接计算得到句子在编码过程中每个位置上的注意力权重,然后再以权重和的形式来计算得到整个句子的隐含向量表示(self attention提出原因:在深度学习领域,模型往往需要接收和处理大量的数据,然而在特定的某个时刻,往往只有少部分的数据是重要的。这种情况下应该让模型更加关注这些重要数据,这样他就可以在计算能力有限的情况下,将计算资源分配给更重要的任务,同时解决信息超载问题)。

       但self attention的缺陷是:模型在对当前位置的信息进行编码时,会过度的将注意力集中于自身的位置, 因此提出了通过多头注意力机制来解决这一问题。

       注:为了更好发挥并行输入的特点首先要解决的问题就是要让输入的内容具有一定的位置信息,因此引入位置编码。

 B、注意力机制

     键值对注意力机制公式如下:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

小型中文版聊天机器人 的相关文章

  • doc2vec 获得良好性能所需的最小数据集大小是多少?

    在不同大小的数据集上进行训练时 doc2vec 的表现如何 原始语料库中没有提到数据集大小 所以我想知道从 doc2vec 中获得良好性能所需的最小大小是多少 有很多东西被称为 doc2vec 但它似乎最常指的是 Le 和 Mikolov
  • 如何使用 python 中的 spacy 库将句子转换为问题 [请参阅下面的我的代码进行更正]

    我需要使用 python 中的 spacy 将任何句子转换为问题 我下面的代码太长了 我需要做更多的工作才能将任何句子完成为问题格式 现在在这段代码中我根据以下条件制定条件是形式 需要形式 有形式 做形式通过检查过去时和现在时 输入 尼娜拉
  • Facebook Messenger Bot 可以与群组中的两个或更多人聊天吗?

    所有消息机器人一次只与一名用户直接交互吗 机器人可以加入两人或多人组成的群组并与他们交谈吗 目前 Facebook Messenger 机器人只能在一对一的基础上工作 原因之一可能是隐私 Facebook 在在群组环境中推出机器人之前非常谨
  • 在非单一维度 1 处,张量 a (2) 的大小必须与张量 b (39) 的大小匹配

    这是我第一次从事文本分类工作 我正在使用 CamemBert 进行二进制文本分类 使用 fast bert 库 该库主要受到 fastai 的启发 当我运行下面的代码时 from fast bert data cls import Bert
  • 下载变压器模型以供离线使用

    我有一个训练有素的 Transformer NER 模型 我想在未连接到互联网的机器上使用它 加载此类模型时 当前会将缓存文件下载到 cache 文件夹 要离线加载并运行模型 需要将 cache 文件夹中的文件复制到离线机器上 然而 这些文
  • SGDClassifier 每次为文本分类提供不同的准确度

    我使用 SVM 分类器将文本分类为好文本和乱码 我正在使用 python 的 scikit learn 并按如下方式执行 Created on May 5 2017 import re import random import numpy
  • 如何计算两个文本文档之间的相似度?

    我正在考虑使用任何编程语言 尽管我更喜欢 Python 来从事 NLP 项目 我想获取两个文档并确定它们的相似程度 常见的方法是将文档转换为 TF IDF 向量 然后计算它们之间的余弦相似度 任何有关信息检索 IR 的教科书都涵盖了这一点
  • 这个 NLP 问题层次结构描述中的最大池化是什么类型

    我正在尝试实现这个描述以及我所做的 我生成了形状的 uni gram bi gram tri gram 15 512 使用填充 然后对于每个单词 我连接三个特征向量 3 512 然后我向他们申请 Globalmaxpooling1D 我不知
  • 如何将 Python 中的 MS Botframework 部署到 Azure

    我一直在玩 Botframework 并尝试从 git 上传示例代码https github com Microsoft botbuilder python tree master samples EchoBot with State ht
  • 从文本文件中提取与输入单词最相似的前 N ​​个单词

    我有一个文本文件 其中包含我使用 BeautifulSoup 提取的网页内容 我需要根据给定的单词从文本文件中找到 N 个相似的单词 流程如下 从中提取文本的网站 https en wikipedia org wiki Football h
  • NLTK 中的无监督 HMM 训练

    我只是想进行非常简单的无监督 HMM 训练nltk http www nltk org 考虑 import nltk trainer nltk tag hmm HiddenMarkovModelTrainer from nltk corpu
  • ANEW 字典可以用于 Quanteda 中的情感分析吗?

    我正在尝试找到一种方法来实施英语单词情感规范 荷兰语 以便使用 Quanteda 进行纵向情感分析 我最终想要的是每年的 平均情绪 以显示任何纵向趋势 在数据集中 所有单词均由 64 名编码员按照 7 分李克特量表在四个类别上进行评分 这提
  • 保存具有自定义前向功能的 Bert 模型并将其置于 Huggingface 上

    我创建了自己的 BertClassifier 模型 从预训练开始 然后添加由不同层组成的我自己的分类头 微调后 我想使用 model save pretrained 保存模型 但是当我打印它并从预训练上传时 我看不到我的分类器头 代码如下
  • 如何将标记化中的多单词名称保留在一起?

    我想使用 TF IDF 特征对文档进行分类 一种方法是 from sklearn feature extraction text import TfidfVectorizer import string import re import n
  • 举例解释bpe(字节对编码)?

    有人可以帮忙解释一下背后的基本概念吗BPE模型 除了这张纸 https arxiv org abs 1508 07909 目前还没有那么多解释 到目前为止我所知道的是 它通过将罕见和未知的单词编码为子词单元序列来实现开放词汇表上的 NMT
  • 斯坦福 CoreNLP:使用部分现有注释

    我们正在尝试利用现有的 代币化 句子分割 和命名实体标记 同时我们希望使用斯坦福 CoreNlp 额外为我们提供 词性标注 词形还原 和解析 目前 我们正在尝试以下方式 1 为 pos lemma parse 创建一个注释器 Propert
  • 管道:多个流消费者

    我编写了一个程序来计算语料库中 NGram 的频率 我已经有一个函数 它消耗一串令牌并生成一个订单的 NGram ngram Monad m gt Int gt Conduit t m t trigrams ngram 3 countFre
  • 使用“自然”语言编写代码更好吗?

    我最近看到一种编程语言叫做超新星 http supernova sourceforge net 他们在网页上说 超新星编程语言是 现代脚本语言和 第一个提出了概念 用直接虚构进行编程 描述使用 纯人类语言的清晰子集 你可以编写如下代码 i
  • 使用 SciKit-learn 和大型数据集进行文本分类

    首先 我昨天开始学习Python 我正在尝试使用 SciKit 和大型数据集 250 000 条推文 进行文本分类 对于该算法 每条推文都将表示为 4000 x 1 向量 因此这意味着输入为 250 000 行和 4000 列 当我尝试在
  • 如何使用FeatureUnion转换PipeLine中的多个特征?

    我有一个 pandas 数据框 其中包含有关用户发送的消息的信息 对于我的模型 我感兴趣的是预测消息的缺失收件人 即给定消息的收件人 A B C 我想预测还有谁应该成为收件人的一部分 我正在使用 OneVsRestClassifier 和

随机推荐

  • Java基础——GUI——Swing中常用容器和组件

    1 swing中常用容器 1 JFrame 常用方法 1 构造方法 2 设置窗体可见 3 设置点击窗体的执行的操作 4 设置窗体的大小和位置 等价于上面两个方法 不管窗体多大 窗体运行起来都会出现在屏幕的中心 5 获取窗体容器 在容器中添加
  • 断点续传和多线程下载

    断点续传和多线程下载 HTTP是通过在Header里两个参数实现的 客户端发请求时对应的是Range 服务器端响应时对应的是Content Range Range 客户端发请求的范围 Content Range 服务端返回当前请求范围和文件
  • fadeOut、fadeIn

    p This is a paragraph p
  • 《Python 黑帽子》学习笔记 - 准备 - Day 1

    信息安全是一个有意思的方向 也是自己的爱好 从零开始 想在工作之余把这个爱好培养为自己的技术能力 而 web 安全相对来说容易入门些 于是选择 web 渗透测试作为学习的起点 并选择同样是容易入门的 Python 作为编程工具 潜心学习 持
  • 卡尔曼滤波算法 C语言实现 示例

    1 概念 卡尔曼滤波 Kalman filtering 是一种利用 k时刻 状态预测值 先验估计值 k 1时刻 状态最优估计值 后验估计值 k时刻 状态预测协方差 先验预测协方差 真实值与预测值之间的协方差 k时刻 状态最优估计协方差 后验
  • C# 字符串去掉括号和括号里面的内容

    using System Text RegularExpressions var majorname 考古学 清华大学 Replace Replace
  • 快速排序和归并排序的相同点和不同点(JAVA)

    首先我们贴出来快速排序的代码 public class QuickSort public int QuickSort int a int left int right int temp a left while left lt right
  • 小程序(十六)消息功能

    文章目录 一 数据库设计 二 系统消息的发送与收取设计 三 业务设计 四 消息实体设计 五 SpringBoot异步任务 1 开启异步注解功能 2 线程池创建 3 异步任务 发送消息 同步获取消息 4 controller 一 数据库设计
  • 字体样式的CSS表示大全

    https blog csdn net kellogg and nina article details 78349654 华文细黑 STHeiti Light STXihei 华文黑体 STHeiti 华文楷体 STKaiti 华文宋体
  • idea中创建xml,xml中路径报错

    1 先创建模板 2 新建xml 3 下面的路径可能会报红 4 解决方法
  • SpringFramework事件与监听机制(监听器)

    SpringBoot版本 2 0 2 RELEASE SpringFramework版本 RELEASE 文章目录 监听者从何而来 来自SpringBoot的监听器 来自SpringFramework的监听器 监听器监听的事件 监听器实现的
  • Linux搭建QT Creator环境

    虚拟机系统 VMware 14 操作系统 Ubuntu14 04 QT版本 4 8 7 1 下载QT 版本 QT4 8 7 路径 http download qt io archive qt 4 8 4 8 7 or https yunpa
  • 阿里云大数据专业认证(ACP级)学习笔记(精简) ...

    阿里云大数据专业认证 ACP级 学习笔记 精简 近百个视频精华考点总结 眼都快肿了 ODPS 流计算用的是ODPS ODPS的所有对象都隶属于项目空间 项目空间project 表table 表中的列支持Bigint长整 Double双精 S
  • Pytorch中tensor与numpy类型数据在GPU和CPU之间的转换

    1 CPU tensor转GPU tensor cpu imgs cuda 2 GPU tensor 转CPU tensor gpu imgs cpu 3 numpy转为CPU tensor torch from numpy imgs 4
  • 华为OD机试-查找充电设备组合-2022Q4 A卷-Py/Java/JS

    某个充电站 可提供n个充电设备 每个充电设备均有对应的输出功率 任意个充电设备组合的输出功率总和 均构成功率集合P的1个元素 功率集合P的最优元素 表示最接近充电站最大输出功率P max的元素 输入描述 输入为3行 第1行为充电设备个数n
  • 论文解读:Personalize Segment Anything Model with One Shot

    发表时间 2023 5 4 项目地址 https github com ZrrSkywalker Personalize SAM 体验地址 https huggingface co spaces justin zk Personalize
  • FFMPEG 合并视频文件(无损)

    FFMPEG 合并视频文件 无损 前言 最近在做视频转码相关业务 但是片源商给了一些DVD的零散视频片段 需要自己去合并片段并进行业务转码 本篇文章主要记录视频合并的过程及常见的合并方式 环境 FFMPEG BUILDS 4 4 1 FFm
  • [Pytorch系列-62]:生成对抗网络GAN - 基本原理 - 自动生成手写数字案例分析

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 121914862 目录 第1章 基本原
  • 这三款软件让你轻松实现在线扫花识别植物

    如今 鲜花是我们日常生活中最常见的植物 但是随着鲜花种类的不断增多 它的许多的种类信息 想必大多数的朋友都难以认识清楚 因此 有的人就会使用一些识别鲜花的APP来帮助我们通过拍照而轻松获知鲜花的信息 那么你们知道识别鲜花的APP都有哪些吗
  • 小型中文版聊天机器人

    入门小菜鸟 希望像做笔记记录自己学的东西 也希望能帮助到同样入门的人 更希望大佬们帮忙纠错啦 侵权立删 目录 一 简单介绍与参考鸣谢 二 数据集介绍 三 数据预处理 1 重复标点符号表达 2 英文标点符号变为中文标点符号 3 繁体字转为简体