# Pass It On

[README-English](README.md)

- 本项目是针对小破站“传下去”梗的一项趣味性研究，涉及文本生成模型

- 本项目提供：数据 + 代码 + 训好的“传下去”生成模型

- 目录
    + [背景](#--)
    + [依赖](#--)
    + [数据](#--)
      - [数据内容说明](#------)
      - [训练数据](#----)
    + [任务描述](#----)
    + [模型描述](#----)
      - [架构](#--)
      - [模型预测](#----)
        * [生成的例子](#-----)
      - [模型训练](#----)
    + [模型局限性](#----)

### 背景

关于小破站“传下去”梗的由来，路过的可能都是老司机，在这里不再赘述，详情可在[小破站](https://www.bilibili.com/s/video/BV1iq4y1W78Z)上观看

### 依赖
~~~
python 3.6
tensorflow 1.14.0
bert4keras 0.10.6
jieba
~~~


### 数据

对照着连续3届的百大Up主名单，以及诸如原神等一些百万粉丝的大号，从视频与动态中爬取了3w条以“传下去”为开头的评论（加上一些有的没的数据过滤与规范化），原始数据以json格式存在了data/corpus.json里，是一个字典的列表

- 举个栗子 
~~~
{
    "context": {
        "bvid": "BV1Gb4y167gE",
        "uid": "476704454"
    },
    "rumors": [
        {
            "source": "热情的舟山人民把小文哥当海鲜吃了",
            "propagations": []
        },
        {
            "source": "5147779123",
            "propagations": [
                "舟山的海鲜把小文哥吃了",
                "舟山的海鲜想让小文哥吃人",
                "热情的小文哥把海鲜当成舟山人民吃了",
                "小文哥热情地把舟山上的海鲜吃了",
                "热情的海鲜在舟山到处吃小文哥",
                "热情的舟山海鲜把小文哥给吃了。",
                "舟山的热情海鲜把小文哥吃了",
                "小文哥带着热情的海鲜把舟山吃了"
            ]
        },
        {
            "source": "小文哥把舟山人民配海鲜吃了",
            "propagations": []
        }
    ]
},
{
    "context": {
        "bvid": "BV1Bw411d7r8",
        "uid": "476704454"
    },
    "rumors": [
        {
            "source": "小文哥吃了兄弟家一山头的桃",
            "propagations": []
        }
    ]
}
~~~
#### 数据内容说明
corpus.json中每一个字典元素包含以下内容：
~~~
context:
    bvid:  # 视频（动态）的id
    uid:   # up主id
rumors:    # 是个列表
    [
        {
            source:  #  是一条评论或评论id (本身也是“传下去”的就是文字，否则就是一串评论id)
            propagations:  #  时间顺序排列的“传下去”评论，包括评论的评论，也都简单列在一起
        },
        {source, propagations},
        {source, propagations},
        ...
    ]
~~~

#### 训练数据
基于原始数据随便构建了用于模型训练的pair数据，即"data/train.samples", 每行格式为"句子1 \t 句子2". 总共29w条数据

### 任务描述
典型生成任务: 
- 输入: 一句话
- 输出: 这句话的“传下去”版本 

这个任务很像同义句生成，但区别在于我们除了句式结构、用词要相似，还需要人为地去曲解原意，使语义发生偏移；此外，从数据中可以看到大部分情况都是一个source能对应N多个“传下去”评论，所以输入和输出属于典型的一对多关系。

### 模型描述
#### 架构
我们利用苏神的同义句生成模型 [roformer-sim](https://github.com/ZhuiyiTechnology/roformer-sim) 作为一个基础，里面有已经训练好的生成+排序同义句模型，是一个很好的开端（站在巨人肩膀上）

针对上面提到的“一对多”关系, 我们仿照百度的对话模型[PLATO](https://arxiv.org/abs/1910.07931)的做法，为模型增加一个隐变量，在输入的序列增加特殊隐变量占位符。于是训练也会有所不同，细节不赘述 

（注：如果山寨的模型代码写的不对劲不要找俺\[旺柴]）

训练好的新模型顺便开源在此，是个12层大小的roformer

[Google Drive](https://drive.google.com/drive/folders/1QY7Lhhi_yNFbau0AlC1SkbRVE-h3s0DO)
 
#### 模型预测
$your_model_path是你下载的模型目录，之后就可以用命令行运行：
~~~
python train_latent.py --do_predict --model_dir=$your_model_path --n=1 --topp=0.8 --text=any_text
~~~
##### 生成的例子
- 输入是"up主喜欢小天使"
~~~
input: up主喜欢小天使
==========  predict:
传下去，up主疑似在天堂被天台感动
传下去，up喜欢天穹
传下去，up主家里有天星
传下去，天使喜欢戴眼镜的up主
传下去，up没有天堂
传下去，up叫天台来烤粉丝。
传下去，up每天早上要直播露脸
传下去，up下天生孩子
传下去，up到天台高产了
传下去，up上天堂了
传下去，up和天使吃了
传下去，楼主的那个帅小伙要穿天堂
传下去，up要cos女巫直播
传下去，天堂大up要爽了
传下去，up下天生孩子翻车了
传下去，up不是人
传下去，天天抽一个up吃8个小孩
传下去，三天之后，up主要被魅魔掏心脏了
~~~
- 输入是"帝君喜欢吃花生米"
~~~
input:  帝君喜欢吃花生米
==========  predict:
传下去，帝君喜欢吃干净的小孩子。
传下去，帝君喜欢捆绑
传下去，帝君用生煎包吃花生米
传下去，帝君吃傻子吃腻了
传下去，喜欢帝君的来自花生米
传下去，花生米吃帝君
传下去，神像吃吃沙子
传下去，我吃帝君屁股
传下去，帝君身边有个米哈游
传下去，帝君只剩干了
传下去，仙跳墙使帝君心疼
传下去，帝君今天上了小通宵
传下去，帝君上床了
传下去，帝君没有下半身
传下去，帝君要炸百京贵妇
传下去，十个视频有了帝君
传下去，帝君会喂食尘神当生日礼物
传下去，视频下一次更新十个帝君
传下去，这个视频里有一年的课代表
~~~
- 输入是"川建国要复辟了"
~~~
input:  川建国要复辟了
==========  predict:
传下去，川建国想要
传下去，川宝上任国君了
传下去，川宝变艾伦了
传下去，《不要传传》
传下去，川宝有天火了。
传下去，阿舅变成了川宝
传下去，川宝长大了也不会忘开
传下去，《川宝要制杖》
传下去，总之，川宝喜欢新衣服
传下去，齐格飞要斩四郎
传下去，老八要吃了川宝
传下去，川普不喜欢制杖
传下去，川团老表是孙笑川
传下去，三叔写盗墓笔记
传下去，川宝没有才浅是制杖
传下去，《川宝喜欢才浅制杖》
传下去，我要吃川宝老爷子
传下去，《我才是川宝喜欢的人》
传下去，全世界辣鸡都不用吃川宝！
传下去，有人冒充川宝想被粉丝上
~~~

#### 模型训练
默认训练10轮，batch_size是128。鼓励事先去下载并使用预训练好的roformer-sim模型。 (e.g., 也就是把第30行改成：checkpoint_path = "chinese_roformer-sim-char-ft_L-12_H-768_A-12")
~~~
python train_latent.py --model_dir=$your_model_dir --train=data/train.samples
~~~

### 模型局限性

既然是个数据驱动的模型，那么当它处理训练语料中从未出现过的新句子，或者内容实在相去甚远的新句子时，就有可能生成奇怪的、不通顺的句子，这都是很正常的。

所以训练数据越多越丰富，生成的效果也就会越好的。欢迎来贡献新语料！