# 破词训练设计文档 ## 背景 输入法用户在实际使用中通常是**逐词输入**的,而非逐字输入。例如输入"那边的特别漂亮的女孩是我的表姐"时,用户可能分词为: ``` 那边 / 的 / 特别 / 漂亮 / 的 / 女孩 / 是 / 我 / 的 / 表姐 ``` 但为了增强模型的泛化能力,需要模拟用户**从词中间断开**的情况。例如用户可能只输入了"漂"就开始选字"亮"。 ## 破词概念 ### 术语定义 | 术语 | 说明 | |------|------| | 整词输入 | 用户输入完整词的拼音,如"piaoliang" | | 破词输入 | 用户只输入词的部分拼音,如"piao" | | 前缀 | 光标前的已确认文本 | | 拼音 | 当前待选字的拼音(可能不完整) | | 后缀 | 光标后的原文内容 | ### 场景示例 以词"漂亮"为例: **整词模式(90%概率):** ``` 光标前: 那边的特别 拼音: piaoliang 预测: 漂 → 亮 ``` **破词模式(10%概率):** ``` 光标前: 那边的特别漂 拼音: liang 预测: 亮 ``` ## 实现方案 ### 分词策略 使用 jieba 分词器进行词语边界识别: ```python import jieba words = list(jieba.cut(text, HMM=False)) # "那边的特别漂亮的女孩是我的表姐。" # → ['那边', '的', '特别', '漂亮', '的', '女孩', '是', '我', '的', '表姐', '。'] ``` ### 两阶段样本生成 每个词生成样本时分为两个阶段: #### Phase 1:前缀/整词阶段 - **整词(90%)**:`prefix_positions = 整个词的所有字符` - **破词前缀(10%)**:`prefix_positions = 词的前 break_pos 个字符` ```python if should_break: break_pos = random.randint(1, word_len_chars - 1) # 随机破开位置 else: break_pos = word_len_chars # 整词 ``` #### Phase 2:破词续接阶段(仅当破词时) 当破词发生时,从断点位置开始继续采样: ```python if should_break and break_pos < word_len_chars: cont_start = char_positions[break_pos] # 从断点开始采样后续字符 target_len = random_choice(1-8) # 采样长度 cont_positions = [cont_start, ...] # 后续字符位置 ``` ### 样本结构 每个字符生成一个训练样本,包含: | 字段 | 说明 | 示例 | |------|------|------| | `part1` (prefix) | 光标前文本 | "那边的特别漂" | | `part2` (pinyin) | 当前字拼音 | "liang" | | `part3` (suffix) | 光标后文本 | "亮的女孩是我的表姐" | | `part4` | 专有词提示 | "漂亮\|特别" | | `label` | 目标汉字ID | 1234 | | `history_slot_ids` | 历史已确认字 | [0, 0, 0, 0, 0, 0, 0, 0] | ### 拼音增强策略 根据 `py_style_weight` 参数,拼音有以下三种形式: | 形式 | 概率 | 示例 | |------|------|------| | 完整拼音 | 75% (9/12) | "piaoliang" | | 仅声母 | 16.7% (2/12) | "pl" (通过 to_initials) | | 仅首字母 | 8.3% (1/12) | "p" | 参数配置:`py_style_weight=(9, 2, 1)` ## 破词概率控制 ### word_break_prob 参数 控制每个词从中间断开的概率,默认为 **10%**: ```python self.word_break_prob = 0.10 # 10%概率从词中间破开 ``` ### 破词位置分布 对于长度为 N 的词,破开位置 `break_pos` 的分布: ```python break_pos = random.randint(1, N - 1) ``` - 2字词:break_pos = 1(100%在第1字后破开) - 3字词:break_pos = 1 或 2(各50%) - 4字词:break_pos = 1, 2, 或 3(各33%) ## 数据分布预期 ### 理想分布 | 类别 | 预期比例 | |------|----------| | 单字样本 | ~15% | | 2字词整词 | ~30% | | 3字词整词 | ~20% | | 破词样本 | ~10% | | 其他 | ~25% | ### 拼音不完整率 由于 `py_style_weight=(9, 2, 1)`: - 声母(initials):~16.7% - 首字母:~8.3% - **总计不完整**:~25% ## 代码实现位置 主要实现文件:`src/model/dataset.py` | 函数/类 | 行号 | 功能 | |---------|------|------| | `segment_text()` | ~30 | jieba分词 | | `build_word_boundaries()` | ~35 | 建立词边界映射 | | `PinyinInputDataset.__iter__()` | ~280 | 核心迭代逻辑 | | `get_mask_pinyin()` | ~215 | 拼音加强处理 | | `_add_word_samples()` | ~240 | 样本构建 | ## 注意事项 1. **破词仅针对多字词**:单字词(如"的"、“是”)不会破词 2. **破词保持语义完整**:破词后仍能根据上下文预测正确汉字 3. **历史槽位模拟逐步确认**:同一词内已确认的字会填入 `history_slot_ids` 4. **10% EOS标记**:词尾有10%概率追加ID=0表示句子结束