Avatar

Qianqiu

Master in PKU Research Direction: VLM, RLHF, MLsys Hobbies: Game, Web novel, Anime

  1. WeChat
  1. Home
  2. Diary
  3. Research
  4. Entertain
  5. Search
  6. Archives
  7. About
    1. Dark Mode Light Mode

Table of contents

  1. Fine-tuning Qwen-1.5B with LoRA for Chinese Sentiment Analysis
    1. AutoModelForSequenceClassification (分类任务)
    2. AutoModelForCausalLM (生成任务)
    3. 原始文本
    4. 分词处理
    5. 完整流程
    6. Why 这么处理
Diary

转码学习day11

Jan 31, 2026

lc:98、51 熬夜看小说起晚了。晚上看了一下claudecode在国内怎么配置的问题,不得不说还真是八仙过海各显神通,什么法子都来了。CC官方防大陆也和防火一样生怕 claudecode在大陆能用。 配了一个小时差不多弄完了,然后把平时写日记的typora换成了obsidian并加上了claudecode插件。 开始做第一个项目:

Fine-tuning Qwen-1.5B with LoRA for Chinese Sentiment Analysis

用Qwen是因为它的中文能力较强,虽然说事实上它支持超过29种语言的多语言。符合我们用lora锦上添花的过程,而且它显存友好,部署起来并不会特别吃力。

特性QwenBERT
架构Decoder-OnlyEncoder-Only
注意力Causal (单向)Bidirectional (双向)
预训练目标自回归生成掩码语言建模
分类时使用最后一个 token[CLS] token

在GPT之前分类任务主流就是用bert,由谷歌2018年提出。 Decoder-Only 模型做分类时,通常取序列最后一个位置的隐藏状态,通过一个线性分类头输出类别概率。和生成式的区别仅仅是最后过的头不同,lora_config = LoraConfig( task_type=TaskType.SEQ_CLS, # 序列分类任务 … ) 不看视频,自己动起手来才发现github的autoxxx还是有点花样的,能够自己帮你处理很多结构。 比如说Qwen2.5-1.5B本来是文本生成的模型,如果用AutoModelForSequenceClassification去下载它,模型结构会自动调整:

AutoModelForSequenceClassification (分类任务)

层级组件名称状态
1Qwen Transformer预训练好的(和上面一样)
2Score Head (2)随机初始化的(num_labels)

AutoModelForCausalLM (生成任务)

层级组件名称状态
1Qwen Transformer预训练好的
2LM Head (151936)预训练好的(词表大小)

关键区别:

  • 分类任务:最后一层是 Score Head,只输出 2 个分数(对应"好"/“坏”)
  • 生成任务:最后一层是 LM Head,输出 151936 个分数(词表中的每个词)

分类任务就不是从超大词表里取出预测的下一个结果了,而是像从两个词的词表中抽一个,也就是"好"或者"坏"。

当然,tokenizer还是要的,虽然最后不用根据它把数据映射回真实词,直接用0、1二分类就行,但是Embedding还是要用tokenizer把数据变成词向量。 今天最后一个工作,理解一下数据预处理。

原始文本

{“text”: “这家酒店环境非常好,服务态度也很棒!”, “label”: 1} {“text”: “服务态度太差了,再也不会来了。”, “label”: 0} 考虑到我们需要的是连贯的文字和可理解的内容。乱码、多余空格、重复标点,都是"脏"的数据,需要去除,但是内容量过于巨大,不可能一个一个看。所以采用下面的操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def clean_text(text: str) -> str:
    # 1. 统一空白字符
    text = re.sub(r'\s+', ' ', text)  # 多个空格/换行 → 单个空格

    # 2. 移除控制字符
    text = re.sub(r'[\x00-\x1f\x7f-\x9f]', '', text)  # 删除不可见字符

    # 3. 标点符号去重
    text = re.sub(r'([。!?,、;:])\1+', r'\1', text)  # "好好好!!!" → "好好好!"

    # 4. 去除首尾空白
    text = text.strip()

第一个传参位置寻找要匹配的字符,第二个位置是替换的结果,第三个位置传入的是文本变量,除此之外还可以传入count,表示最多替换次数,不传则替换所有。flags,正则表达式标志,比如忽略大小写之类的操作可以由它指定。

分词处理

模型不能直接理解文字,需要转换成数字

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def preprocess_function(examples, tokenizer, max_length=256):
    # 1. 清洗文本
    texts = [clean_text(text) for text in examples["text"]]

    # 2. 分词:文字 → 数字
    tokenized = tokenizer(
        texts,
        padding="max_length",    # 填充到固定长度
        truncation=True,         # 超长就截断
        max_length=max_length,   # 最大长度256
        return_tensors=None,     # 返回Python列表
    )

以 “这家酒店很好” 为例:

词汇切分: “这家酒店很好” → [“这家”, “酒店”, “很”, “好”] 转换ID: [“这家”, “酒店”, “很”, “好”] → [1234, 5678, 9012, 3456] 添加特殊token: [101, 1234, 5678, 9012, 3456, 102] (101=开始,102=结束),4个转换的token,加俩特殊token 填充: 如果不足256长度,用0填充到256 结果包含:

  • input_ids: [101, 1234, 5678, 9012, 3456, 102, 0, 0, ...] (256个数字)
  • attention_mask: [1, 1, 1, 1, 1, 1, 0, 0, ...] (1=真实内容,0=填充)
  • labels: 1 (情感标签) 接着用一个函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def create_tokenized_dataset(dataset, tokenizer, max_length=256):
    # 定义处理函数
    def tokenize_fn(examples):
        return preprocess_function(examples, tokenizer, max_length)
    
    # 并行处理所有数据
    tokenized_dataset = dataset.map(
        tokenize_fn,
        batched=True,           # 批量处理,更快
        num_proc=4,            # 4个进程并行
        remove_columns=[...],   # 删除原始text列,只保留数字
        desc="Tokenizing dataset",
    )

批量调用刚刚的预处理过程即可,dataset.map封装好了最后的组装环节,直接输入各个参数即可。

完整流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
原始数据:
{"text": "这家酒店很好", "label": 1}

↓ clean_text
"这家酒店很好" (清洗后)

↓ tokenizer
{
  "input_ids": [101, 1234, 5678, 9012, 3456, 102, 0, 0, ..., 0],  # 256个
  "attention_mask": [1, 1, 1, 1, 1, 1, 0, 0, ..., 0],           # 256个
  "labels": 1
}

↓ 输入模型训练

Why 这么处理

  1. 统一格式: 所有句子都变成256长度的数字数组
  2. 批量处理: 可以一次性处理多个样本,提高效率
  3. 模型理解: 模型只能处理数字,不能直接理解文字
  4. 内存对齐: 固定长度便于GPU并行计算 今天先到这吧,等着晚点看cs2的2026年的森载大战了。

Related content

学习day28:

学习day27:

学习day26:

学习day25:

未学习day24:计划赶不上变化,又摆一天

© 2025 - 2026 Qianqiu
Built with Hugo
Theme Stack designed by Jimmy