点我展开:基本信息

通过微调一个文言文翻译助手,熟悉多显卡服务器的使用及在改服务器上大模型的部署。

服务器使用

实验室服务器需要远程连接,通过一个中转进行连接,命令如下:

ssh xiaoyao@yxy.pub -p 25
# 之后输入密码:

对于一个新申请的账号,最好按照实验室标准进行目录管理。

nvidia-smi

rm -rf 强制删除

watch -n 2 -d nvidia-smi

ls -lt –time-style=long-iso checkpoint-* 查看文件什么时候创建的

du -sh . 当前文件夹多大


/home/your_id/
├── workspace/          # 活跃项目代码(每日同步到 GitLab)
│   └── project_1/
├── data/               # 个人数据集(软链接到 /data/your_id 以节省 home 配额)
│   └── dataset_1 -> /data/your_id/dataset_1
├── scripts/            # 常用脚本(如自动化备份、环境配置)
├── software/           # 本地编译的软件(如 ~/software/gcc-12.1)
└── logs/               # 作业日志(按日期归档,定期清理)
cd ~
mkdir -p workspace data archive logs software
from datasets import load_dataset
test_dataset = load_dataset("YeungNLP/firefly-train-1.1M", split="train[:1000]")

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-3B-Instruct")

def format_prompt(example):
    chat = [
        {"role": "system","content": "你是一个非常棒的人工智能助手。"},
        {"role": "user", "content": example["input"]},
        {"role": "assistant", "content": example["target"]}
    ]
    prompt = tokenizer.apply_chat_template(chat, tokenize=False)
    return {"text": prompt}

dataset = test_dataset.map(format_prompt, remove_columns=test_dataset.column_names)
dataset

from transformers import AutoModelForCausalLM
import torch

model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-3B-Instruct", device_map="auto")

tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-3B-Instruct")
tokenizer.padding_side = "left"

from peft import LoraConfig, get_peft_model

peft_config = LoraConfig(
    lora_alpha = 32,
    lora_dropout = 0.1,
    r = 64,
    bias = "none",
    task_type = "CAUSAL_LM",
    target_modules = ['k_proj', 'v_proj', 'q_proj']
)

model = get_peft_model(model, peft_config)


from transformers import TrainingArguments

output_dir = "./results"

training_arguments = TrainingArguments(
    output_dir = output_dir,
    per_device_train_batch_size = 2,
    gradient_accumulation_steps = 4,
    optim = "adamw_torch",
    learning_rate = 2e-4,
    lr_scheduler_type = "cosine",
    num_train_epochs = 3,
    logging_steps = 50,
    fp16=True,
    gradient_checkpointing=False,

    save_steps=200,
    max_steps=-1,
)

from trl import SFTTrainer

trainer = SFTTrainer(
    max_seq_length=50,
    model=model,
    args=training_arguments,
    dataset_text_field="text",
    train_dataset=dataset,
    tokenizer=tokenizer,
    peft_config=peft_config,
)

trainer.train()

trainer.model.save_pretrained("./results/final-result")

下面为一个class中的方法,修改前:


将其单列出来作为一个class进行修改,修改后:

# ==========================
# 1. 关掉 TF 垃圾日志
import os, warnings
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
warnings.filterwarnings("ignore", category=UserWarning, module="pydantic.*")

# ==========================
# 2. 卸载可能冲突的高版本 protobuf / TF 相关包
import sys
!{sys.executable} -m pip uninstall -y protobuf tensorflow tensorflow-metadata apache-beam

# ==========================
# 3. 安装 Kaggle 官方源里兼容的组合
!&#123;sys.executable&#125; -m pip install --upgrade "protobuf==4.25.2" "tensorflow==2.15.1" "datasets>=2.14" "trl>=0.9.0,<0.10" --quiet

# ==========================
# 4. 立即重启 kernel(Kaggle 需要手动点 Restart)
print("✅ 依赖修复完成,请重启 kernel(菜单 Kernel → Restart)后再运行下方代码!")

from datasets import load_dataset
test_dataset = load_dataset("YeungNLP/firefly-train-1.1M", split="train[:1000]")

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-3B-Instruct")

def format_prompt(example):
    chat = [
        &#123;"role": "system","content": "你是一个非常棒的人工智能助手。"&#125;,
        &#123;"role": "user", "content": example["input"]&#125;,
        &#123;"role": "assistant", "content": example["target"]&#125;
    ]
    prompt = tokenizer.apply_chat_template(chat, tokenize=False)
    return &#123;"text": prompt&#125;

dataset = test_dataset.map(format_prompt, remove_columns=test_dataset.column_names)
dataset

from transformers import AutoModelForCausalLM
import torch

model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-3B-Instruct", device_map="auto")

tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-3B-Instruct")
tokenizer.padding_side = "left"

大模型微调

主要分为模块导入,...,对demo_class.py编写进行规范。

导入模块

核心要求是:标准库 → 第三方库 → 本地模块,各分组之间空一行,每组内部按字母序升序排列,绝对禁止 import *,class与导入模块相隔2行。

import logging
from typing import List

import pandas as pd
from docx import Document
from openai import OpenAI

from core.config import LLM_MODEL
from utils.logger import logger

注:不能一行导入多个包。

命名与可见性

主要对成员实例变量、方法、常量进行规范。前者为共有,后者为内部/私有。

  • 实例变量:user_name,_user_name
  • 方法:compute_total(),_compute_total()
  • 常量:MAX_RETRY,_MAX_RETRY

类方法

类方法分为@staticmethod与@classmethod,前者可以理解为只是一个普通函数“借住”在类里,跟类没有任何绑定,无法访问类级别的任何信息。后者为类方法,可以访问类级别的任一信息,两者在使用时均可以用cls.method(),类名称直接点方法调用,实例方法与@classmethod属性类似,均可以访问类级别的任一信息,但是实例方法必须实例化,obj.method(),实例对象点方法调用。还有一个普通类属性,直接cls.arg调用,四者相互配合,下面以一个实际例子来展现它们之间的关系。

from __future__ import annotations
from dataclasses import dataclass, asdict
from decimal import Decimal
import datetime

@dataclass
class Order:
    user_id: int
    amount_cents: int          # 以分为单位
    status: str = "PENDING"

    # ---------- 1. 实例方法:操作“这张订单” ----------
    def pay(self) -> None:
        if self.status != "PENDING":
            raise ValueError("Only PENDING order can pay")
        self.status = "PAID"
        # 更新类级统计
        Order._today_turnover_cents += self.amount_cents

    def cancel(self) -> None:
        self.status = "CANCELLED"

    # ---------- 2. 类方法:工厂 + 统计 ----------
    _today_turnover_cents: int = 0          # 类级属性:今日已付款总额

    @classmethod
    def from_json(cls, data: dict) -> Order:
        """反序列化 JSON -> Order 对象"""
        return cls(
            user_id=int(data["user_id"]),
            amount_cents=int(data["amount_cents"]),
            status=data.get("status", "PENDING")
        )

    @classmethod
    def today_turnover_yuan(cls) -> Decimal:
        """取今日已付款总额(单位:元)"""
        return Decimal(cls._today_turnover_cents) / 100

    # ---------- 3. 静态方法:纯工具 ----------
    @staticmethod
    def cents_to_yuan(cents: int) -> Decimal:
        """分 → 元,与任何订单实例无关"""
        return Decimal(cents) / 100

    # ---------- 4. 普通类属性:回调 / 钩子 ----------
    # 把“序列化方式”挂到类上,外部可替换(策略模式)
    dump_fn = asdict
    
Order.dump_fn = o1

Q:什么时候选择实例方法,什么时候选择类方法?
A:当逻辑需要类级共享数据,例如,订单总数,选择类方法,否则均选择实例方法,主要有如下原因:面向对象的核心是“对象”可扩展性更好线程/并发更安全(类属性是全局共享的;实例属性隔离在线程各自的实例里,天然避免竞态条件。)
Q:普通类属性的作用?
A:可以对类级信息进行修改,以此通过外部替换策略模式。

cls注释规范

在class对应位置添加注释可使得cls有更好的可读性。

class Order:
    """单笔订单的业务对象.

    负责状态机、金额计算与持久化。

    Attributes:
        order_id: 全局唯一订单号.
        amount_cents: 订单金额(单位:分).
        status: 订单状态,取值见 OrderStatus 枚举.
    """
    
    def __init__(
        self,
        order_id: str,
        items: List[str],
        coupon: Optional[str] = None,
    ) -> None:
        ...
    

class DocumentationExtractor:
    """
    从 Word 文档中提取并识别特定表格的通用工具类。
    """

    @staticmethod
    def _extract_all_tables(file_path: str) -> List[pd.DataFrame]:
        """
        读取 Word 文档中的所有表格,并统一转换为 pandas.DataFrame 列表。

        Args:
            file_path (str): Word 文档绝对路径。

        Returns:
            List[pd.DataFrame]: 文档中所有表格对应的 DataFrame 列表。
        """
  1. 对于类,在正式编写之前要加入类的功能说明及公共属性说明;
  2. 对于类中的函数,参数要指明输入的类型,后面同时要指出输出的类型,整体格式为竖列,在正式编写之前要加入相关说明,说明主要包含三个部分,函数功能说明、参数说明、返回值说明。

提供__main__示例

自包含可运行的示例,Python 解释器启动后,会给每个模块(.py)自动设置一个全局变量__name__,如果该文件为主程序值为__main__,若该文件是被import,则值变为模块名。当文件为主程序时,运行函数下的代码,若不为主程序则忽略;

# 使用示例
if __name__ == "__main__":
    content = DocumentationExtractor.identify_specific_table(
        file_path="/abs/path/云南电网有限责任公司安全文明施工费使用管理标准(试行).docx",
        expected_columns=[
            "费用类型及取费建议",
            "措施类别及使用范围",
            "措施类别编码",
            "是否可摊销",
            "变电",
            "线路",
            "配网",
        ],
        expected_fee_types=["安全生产费", "文明施工费", "环境保护费"],
    )
    print(content)

SZUer继续加油!