封面

qwopus3.5-9b-fanren-lora

基于 Qwen3.5-9B-Text-Only-abliterated(去视觉塔)模型,使用 LoRA 以《凡人修仙传》全文微调的中文仙侠小说续写模型。强烈建议阅读零基础教程,本项目是零基础教程的配套代码与模型库。

HuggingFace GitHub Blog


快速开始

0. 获取代码与模型

# 克隆仓库(含 LoRA 适配器 + 全部脚本)
git clone https://huggingface.co/zjml/Qwen3.5-9B-Fanren-LoRA
cd Qwen3.5-9B-Fanren-LoRA

基座模型 Qwen3.5-9B-Text-Only-abliterated ,已配置自动下载,但仍建议手动下载备好,防止网络波动造成的时间浪费

1. 配置环境

# 创建虚拟环境(conda 或 venv 二选一)

# 方式 A:conda
conda create -n fanren python=3.12 -y
conda activate fanren

# 方式 B:venv
python -m venv venv
venv\Scripts\activate       # Windows
# source venv/bin/activate  # Linux / macOS

2. 安装依赖

注意安装顺序! unsloth 会强制拉取 CPU 版 PyTorch,必须先装 CUDA 版再装 unsloth 并禁止它动依赖:

# 1. 先装 CUDA 版 PyTorch
pip install torch==2.13.0+cu132 --index-url https://download.pytorch.org/whl/cu132 --force-reinstall
pip install torchvision --index-url https://download.pytorch.org/whl/cu132

# 2. 再装 bitsandbytes
pip install bitsandbytes

# 3. 最后装 unsloth,禁止自动拉依赖
pip install unsloth==2026.7.1 --no-deps
pip install unsloth-zoo==2026.7.1 --no-deps

# 4. 其余依赖正常安装
pip install transformers trl datasets peft

Windows 用户注意:

  • 训练前务必设置 UTF-8 模式:set PYTHONUTF8=1 && python train.py(代码已内置自动重启,直接运行即可)
  • bitsandbytes 报 CUDA DLL 缺失,参考下方 常见问题 中的解决方案。

3. 一键推理

克隆后直接运行(基座模型和 LoRA 适配器均自动加载):

# 使用默认测试案例(基座模型首次会自动下载)
python inference.py

# 列出所有内置测试案例
python inference.py --list

# 按编号选择
python inference.py -c 3          # 修仙突破
python inference.py -c 5          # 斗法激战

# 按关键词选择
python inference.py -c "天劫"
python inference.py -c "炼丹"

# 自定义前文
python inference.py -i "韩立独自行走在乱星海的夜空之下——"

更多选项

python inference.py -c 12                         # 大乘对决(预设高创意度)
python inference.py -c 5 --temperature 1.2        # 覆盖温度
python inference.py -i "天南第一剑修睁开了双眼——" --temperature 1.2 --max-tokens 1024
参数 说明 默认值
-c / --case 内置案例编号 (1-12) 或名称关键词 案例 1
-i / --input 前文内容(覆盖 --case)
--list 列出全部内置案例
-n / --max-tokens 最大生成长度 512(可被案例覆盖)
--temperature 创意度(越高越大胆) 0.8(可被案例覆盖)
--top-p 核采样阈值 0.9
-l / --lora LoRA 适配器路径 lora/qwen3.5-9b-text-lora-checkpoint

内置测试案例

编号 名称 温度 场景
1 入门测试 0.8 七玄门演武场
2 灵药奇遇 0.8 小绿瓶催生灵药
3 修仙突破 0.7 丹田突破境界
4 坊市淘宝 0.9 黄枫谷捡漏
5 斗法激战 0.8 飞剑斗法
6 秘境探险 0.85 传送阵进秘境
7 炼丹失败 0.8 丹炉炸锅
8 高阶对话 0.85 元婴老怪召见
9 绝境逃生 0.9 妖兽追杀
10 天劫降临 0.8 结丹渡劫
11 心魔幻境 0.85 幻境中的家人
12 大乘对决 1.2 天南剑修巅峰战 (1024 tokens)

环境要求

  • Python 3.10+,CUDA 12.4+
  • 显存 ≥ 8 GB(4-bit 量化)

Python API 调用

from unsloth import FastLanguageModel
import torch

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="zjml/Qwen3.5-9B-Text-Only-abliterated",
    max_seq_length=1280,
    dtype=torch.bfloat16,
    load_in_4bit=True,
)
model.load_adapter("zjml/Qwen3.5-9B-Fanren-LoRA")
model.to("cuda")  # 确保 LoRA 权重在 GPU
FastLanguageModel.for_inference(model)

# 生成续写
prompt = """### Instruction:
你是仙侠小说《凡人修仙传》风格的作家,请根据以下前文内容,续写接下来的剧情。

### Input:
韩立盘膝坐在洞府之中,双手掐诀,体内真气缓缓流转——

### Response:
"""
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=512, temperature=0.8, do_sample=True)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

项目结构

qwopus3.5-9b-fanren-lora/
├── README.md                     # 本文件
├── inference.py                  # 推理脚本:内置 12 个测试案例,流式生成续写
├── split_novel.py                # 数据预处理:将小说 TXT 切割为 instruction-input-output 格式的 JSON
├── train.py                      # 训练脚本:Unsloth LoRA SFT 微调,含 4-bit 量化、日志记录
├── strip_vision.py               # 工具脚本:从 VLM 模型中剥离视觉塔
├── .gitignore
├── model/
│   └── Qwen3.5-9B-Text-Only-abliterated/  # 本地基座模型(去视觉塔版)
├── lora/
│   └── qwen3.5-9b-text-lora-checkpoint/   # LoRA 训练输出(116 MB 适配器)
├── logs/                         # 训练日志
├── fan_ren.json                  # 训练数据集(由 split_novel.py 生成)
├── 凡人修仙传.txt                  # 小说原文(需自行准备)
└── unsloth_compiled_cache/       # Unsloth 编译缓存

训练细节

硬件配置

项目 详情
GPU NVIDIA GeForce RTX 5070 (12 GB VRAM)
OS Windows 11
Python 3.12.13
PyTorch 2.13.0+cu132
CUDA 13.2
unsloth 2026.7.1
bitsandbytes 0.49.2
transformers 5.3.0

训练参数

参数
基座模型 Qwen3.5-9B-Text-Only-abliterated(去视觉塔,纯文本,FastLanguageModel)
加载方式 4-bit 量化 (BitsAndBytes)
LoRA rank (r) 16
LoRA alpha 16
LoRA dropout 0.05
目标模块 q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj
最大序列长度 1280(利用去视觉塔节省的 ~1 GB 显存,逐步从 768→1024→1280)
有效 batch size 4 (1 × grad_accum=4)
学习率 2e-4
热身步数 10
优化器 adamw_8bit
精度 bf16
早停 patience=3, metric=eval_loss
验证集比例 5%

训练结果

指标
训练数据 《凡人修仙传》全文
数据格式 380+380 字符/条(约 1280 tokens)
训练样本数 19,034
训练轮数 3 epochs(实际 77% 即停止)
目标步数 14,277
实际步数 11,000(Early Stopping 触发)
最佳步骤 checkpoint-9500
起始 eval_loss 3.03
最佳 eval_loss 2.19(-27.7%)
最终 train_loss 1.61
可训参数 29M(占基座模型的 0.32%)
适配器大小 116 MB

eval_loss 走势:

Step eval_loss 变化
500 3.0326
1000 2.9383
2000 2.8086
3000 2.7207
4000 2.6516
5000 2.5587
6000 2.4894
7000 2.3659
8000 2.2783
9000 2.2107
9500 2.1899 ↓ 最佳
10000 2.2140 ↑ 反弹
10500 2.1984
11000 2.1924 ↑ → Early Stop

模型在 9500 步达到最佳 eval_loss=2.19,之后连续 3 次无法超越,Early Stopping 自动终止。


生成样例

以下是 LoRA 微调后模型的实际生成效果(未人工编辑)。

样例 1:入门测试(案例 1, temp=0.8)

输入:韩立站在七玄门的演武场上,周围站满了前来参加入门测试的少年。他握紧了拳头,手心微微出汗——

生成: 这是他第一次面对这么多人,而且这些人都是和他差不多大的同龄人。 "咳!真没想到,自己竟然会出现在这个地方。"韩立有些后悔了。 就在昨天,他还和好友张铁在山上采药,结果因为天色已晚,两人就随便找了一处较隐蔽的山崖休息了一会儿。可谁知,他们刚躺下没多久,就听到一阵吵嚷声从附近传来。 好奇心的驱使,让二人爬起身来,悄悄凑了过去。 结果,韩立和张铁看到了令他们目瞪口呆的一幕:一个身穿黄衫的汉子,正被七八个身穿绿衣的人团团围住,而那汉子虽然身手不弱,但毕竟单打独斗,自然不是群殴的对手,眼看就要不支了。 看到这一幕,韩立心里有些不忍,就和张铁商量了一下后,悄悄的潜了上去。 "啪!"韩立用一把小刀,把一名绿衣人的喉咙给割了下来。

样例 2:斗法激战(案例 5, temp=0.8)

输入:对面的黑衣修士冷笑一声,张口喷出一把乌黑色的飞剑,剑身在空中一个盘旋,化作一道黑虹直奔韩立面门而来。韩立面色不变,单手一抬,一面青色小盾滴溜溜飞出——

生成: ,挡在了身前。 "噗嗤"一声轻响,黑色剑光一闪即逝的斩到了青色盾牌上,盾牌表面青光狂闪几下后,就若无其事地反弹出尺许长的青芒,一下将黑芒击飞了出去。 黑衣修士脸色微变,但随即嘴角露出一丝狞笑,另一只手也冲空中一招,那口金色飞剑立刻化为一道金虹,狠狠斩向韩立。 韩立眉梢一动,身形一晃,人就从原地消失不见,下一刻出现在了十余丈外的地方。 而几乎与此同时,一道金光从原处激射而出,在途中突然爆裂开来,无数道纤细金丝密密麻麻的朝四面八方迸射出去。

样例 3:炼丹失败(案例 7, temp=0.8)

输入:韩立小心翼翼地揭开丹炉的盖子,一股焦糊味扑面而来——炉底躺着三颗乌黑干瘪、毫无灵光的丹丸。他苦笑着摇了摇头,这已是第三次失败了。每一炉材料都价值不菲,再这样下去——

生成: "砰"的一声巨响,韩立手中的铜锤猛然砸在了地面上,整个人也晃了一晃,差点没站稳住脚步。 "怎么回事?"韩立心里一惊,有些纳闷起来。 "你……"韩立刚想开口说些什么时,身后却传来了一声叹息声。 "怎么?连自己炼制丹药失败都不肯承认吗?"一个男子的声音从后面悠悠传来,接着身后还传来了脚步声。 韩立心里一凛,急忙转过身来,只见在离他数丈远的地方,正站着一位三十许岁的中年人。此人面目普通,但两眼清澈明亮,给人一种很舒服的感觉。 "阁下是谁?"韩立强压住心中的惊骇,冷静的问道。


自己训练

步骤 1:准备数据

⚠️ 原始文本需要自行准备。 本项目不提供小说原文,请将你的 TXT 文件放置到项目根目录后再进行切割。

将小说 TXT 文件切割为训练样本:

python split_novel.py --input "凡人修仙传.txt" --output "fan_ren.json" --input-len 380 --output-len 380 --instruction "你是仙侠小说《凡人修仙传》风格的作家,请根据以下前文内容,续写接下来的剧情,要求文风一致,逻辑连贯。"

参数说明:

参数 说明 默认值
-i / --input 输入 TXT 文件路径 凡人修仙传.txt
-o / --output 输出 JSON 文件路径 fan_ren.json
--input-len 前文(输入)长度(字符) 380
--output-len 续写(输出)长度(字符) 380
--step 窗口滑动步长(字符),小于 input-len 则有重叠 = input-len
--instruction 固定的系统指令 凡人修仙传默认指令

上下文匹配说明:380 字符 × ~1.5 tokens/中文字 + 模板开销 ≈ 1230 tokens,匹配 MAX_SEQ_LENGTH=1280,利用率 96%。

步骤 2:修改训练配置

编辑 train.py,修改以下配置:

JSON_DATA_PATH = "./your_training_data.json"     # 你的训练数据
LORA_SAVE_DIR  = "./lora/your-model-checkpoint"  # 输出路径

步骤 3:开始训练

python train.py

训练日志会实时输出到控制台和 ./logs/ 目录。


安装踩坑记录

以下是在 Windows + CUDA 13.2 环境下配置训练环境时遇到的真实问题及解决方案。

坑 1:pip install unsloth 会把 PyTorch 降级成 CPU 版

现象: torch.cuda.is_available() 返回 Falsetorch.__version__ 显示 2.10.0+cpu

原因: unsloth-zoo 依赖 torch<2.13.0,直接 pip install unsloth 会把 CUDA 版 PyTorch 替换为更早的 CPU 版本。

解决: 先装 CUDA 版 PyTorch,再用 --no-deps 装 unsloth,禁止它动依赖:

pip install torch==2.13.0+cu132 --index-url https://download.pytorch.org/whl/cu132
pip install unsloth==2026.7.1 --no-deps
pip install unsloth-zoo==2026.7.1 --no-deps

坑 2:bitsandbytes 缺少 CUDA 13.2 的 DLL

现象: libbitsandbytes_cuda132.dll not found,bnb 量化操作报错。

原因: bitsandbytes 0.49.2 最高预编译到 cuda130,没有 cuda132

解决: CUDA 小版本二进制兼容,复制一份即可:

copy %CONDA_PREFIX%\Lib\site-packages\bitsandbytes\libbitsandbytes_cuda130.dll ^
     %CONDA_PREFIX%\Lib\site-packages\bitsandbytes\libbitsandbytes_cuda132.dll

坑 3:VLM 视觉塔浪费显存

现象: 使用 VLM 模型(如 Huihui-Qwopus3.5-9B-v3-abliterated)训练纯文本任务时,视觉编码器白白占用约 1 GB 显存,限制了上下文长度。

原因: VLM 模型被 unsloth 识别为视觉模型,必须用 FastVisionModel 加载,即使训练数据是纯文本,视觉塔也会被加载进显存。

解决: 换用去视觉塔的纯文本版 Qwen3.5-9B-Text-Only-abliterated,使用 FastLanguageModel 加载:

from unsloth import FastLanguageModel

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="Qwen3.5-9B-Text-Only-abliterated",
    max_seq_length=1280,     # 省下 1 GB → 上下文从 768 提升到 1280
    load_in_4bit=True,
)

切换后显存约 10.3 GB,seq_len 从 768 逐步调到 1280,显存仅增加 0.2 GB,得益于 unsloth 的 padding-free 和 gradient checkpointing 优化。

坑 4:UnicodeDecodeError: 'gbk' 编码错误

现象: PyTorch 内部读 CUDA kernel 模板时报 'gbk' codec can't decode byte 0x94

原因: Windows 默认 GBK 编码,而 PyTorch 的 .py 文件含 UTF-8 字符。PYTHONUTF8 环境变量必须在解释器启动前设置才生效。

解决: train.pyinference.py 内置了自动重启逻辑——检测 sys.flags.utf8_mode,如果不是 UTF-8 模式则 os.execv 重新启动。

坑 5:首次训练 checkpoint 报错

现象: ValueError: No valid checkpoint found in output directory

原因: trainer.train(resume_from_checkpoint=True) 要求输出目录已有 checkpoint,首次训练时目录为空。

解决: train.py 启动前自动检测 checkpoint-* 目录,无则从零开始。

坑 6:推理时 LoRA 权重在 CPU 上报错

现象: RuntimeError: Expected all tensors to be on the same device, but got mat2 is on cpu

原因: model.load_adapter() 加载 LoRA 权重后,适配器参数留在 CPU,而 4-bit 基座模型在 GPU。

解决: 加载适配器后调用 model.to("cuda") 确保 LoRA 权重移到 GPU:

model.load_adapter("lora/qwen3.5-9b-text-lora-checkpoint")
model.to("cuda")  # 关键!
FastLanguageModel.for_inference(model)

常见问题

Q: 基座模型是什么?

基座模型是 Qwen3.5-9B-Text-Only-abliterated,从 Huihui-Qwopus3.5-9B-v3-abliterated(VLM)剥离视觉编码器后的纯文本版本。基于 Qwen3.5-9B 的 abliterated 版本,支持中英文,Apache-2.0 许可。省下的 ~1 GB 视觉塔显存用于提升上下文长度(768→1024→1280)。

Q: 为什么选择 LoRA 而不是全参数微调?

全参数微调 9B 模型需要 > 40 GB 显存。LoRA 仅训练 0.32% 的参数(29M,116 MB),在 RTX 5070 的 12 GB 显存上即可完成。

Q: 为什么 loss 在 2.2 左右就不下降了?Eval loss 1.5 不更好吗?

对于中文文学创作类任务,eval_loss 在 2.0~2.5 之间属于正常范围。我们的训练在 2.19 触发了 Early Stopping,表明模型已学习到了有效模式但未过拟合原文。loss 过低(<1.5)反而可能导致过拟合(逐字背诵原文),影响创作多样性。

Q: 上下文为什么从 768 调到 1280?

切换去视觉塔模型后节省了 ~1 GB 显存。由于 unsloth 的 padding-free 和 gradient checkpointing 优化,seq_len 从 768→1024→1280 显存仅增加 ~0.2 GB。1280 tokens 与数据格式 (380+380 字) 匹配度达 96%,是当前显存限制下的最佳平衡点。


许可

  • 基座模型:Apache-2.0(Qwen3.5-9B-Text-Only-abliterated)
  • LoRA 适配器:Apache-2.0
  • 训练脚本:MIT
Downloads last month
194
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for zjml/Qwen3.5-9B-Fanren-LoRA