人工智能:语言大模型训练与部署流程

Gemini是Google开发的一款多模态大模型,能够处理文本、图像、音频、视频和代码等信息。目前推出的Gemini模型分为Nano、Pro、Ultra以及1.5Pro,这些模型都可以在谷歌网站上进行访问:https://gemini.google.com 。此外,谷歌还提供了Gemini模型的API,可在代码中调用模型,输入文本和图片然后,输出文本回复,以及一款开源大语言模型gemma,该模型基于Gemini的研究和技术开发,能处理文本信息。模型有2b和7b两种参数规模以及经过指令调优(2b-it & 7b-it)和未调优的基础模型等版本,可通过多种框架构建:Keras、Pytorch、Transformers、Gemma C++、TensorRT-LLM、TensorFlow Lite、MaxText、Pax、Flax。感谢我过去的学生 Weizheng Wang, Hui Wu 对本文的贡献。

环境配置

配置 CentOS Stream 10

本文选用 CentOS Stream 10 操作系统,内核版本 6.12.0,并配置网络环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ uname -r                        # 查看内核版本
6.12.0-89.el10.x86_64

$ yum --exclude=kernel* update # 禁用 yum update,避免 kernel 更新导致的生产环境不稳定风险

$ nmcli connection show
NAME UUID TYPE DEVICE
enp0s31f6 52eaa752-25be-451f-a8c1-4037030e4cb4 ethernet enp0s31f6
lo 4d717253-e93b-404c-9487-38d706cb9308 loopback lo
enp2s0 7ef8d2c6-cd0a-3123-9fef-4218782fcf1b ethernet --

$ cd /etc/NetworkManager/system-connections/
$ su vi enp0s31f6.nmconnection # 编辑对应网卡的配置文件

编辑 enp031f6.connection 文件,示例信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[connection]
id=enp0s31f6
uuid=52eaa752-25be-451f-a8c1-4037030e4cb4
type=ethernet
autoconnect-priority=-999
interface-name=enp0s31f6
timestamp=1748939249

[ethernet]

[ipv4]
address1=192.168.0.125/24
dns=192.168.0.1;
gateway=192.168.0.1
method=manual

[ipv6]
addr-gen-mode=eui64
method=auto

[proxy]

修改完成后重新启动网络服务:

1
2
3
4
5
6
7
8
9
10
$ systemctl restart NetworkManager
$ nmcli c reload # 重新加载配置文件
$ nmcli c up enp031f6 # 重启ens33网卡

$ ping www.baidu.com -c4 # 验证网络连接
PING www.a.shifen.com (39.156.66.14) 56(84) bytes of data.
64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=1 ttl=50 time=38.5 ms
64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=2 ttl=50 time=38.2 ms
64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=3 ttl=50 time=38.2 ms
64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=4 ttl=50 time=38.2 ms

配置硬盘分区永久挂载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ lsblk   # 查看磁盘硬件
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 21.8T 0 disk /mnt/disk1
sdb 8:16 0 14.6T 0 disk /mnt/disk0
sdc 8:32 0 21.8T 0 disk /mnt/disk2
nvme0n1 259:0 0 1.8T 0 disk
├─nvme0n1p1 259:1 0 600M 0 part /boot/efi
...

$ fdisk /dev/sda # 修改该磁盘中的分区
$ mkfs.ext4 /dev/sda # 按 ext4 文件系统格式化分区
$ mkdir /mnt/mydrive # 创建挂载路径
$ mount /dev/sda /mnt/mydrive # 临时挂载该分区

$ blkid /dev/sda # 查看该磁盘的UUID与文件系统格式
/dev/sda: UUID="E600-A571" BLOCK_SIZE="512" TYPE="exfat"

$ vi /etc/fstab # 修改 /etc/fstab 文件永久挂载磁盘
UUID=04c21771-726f-42d0-a7d3-34f4b5cd88ab / xfs defaults 0 0
UUID=246017ac-59da-4dd2-9493-dbcba61a2958 /boot xfs defaults 0 0
UUID=AD2B-E205 /boot/efi vfat umask=0077,shortname=winnt 0 2
UUID=35b358b4-d3c7-443f-866d-8db5d364a46b /home xfs defaults 0 0
UUID=521947e5-c326-4436-9e2d-26a148d8f42f none swap defaults 0 0
UUID=40948978-7d0f-4e63-9feb-ffa7ab03f2a4 /mnt/disk0 ext4 defaults 0 0
UUID=E600-A571 /mnt/disk1 exfat defaults 0 0
UUID=C839-78EB /mnt/disk2 exfat defaults 0 0

$ systemctl daemon-reload # 修改 fstab 文件后重启 daemon

为用户名为 hwchai 的普通用户创建 Ed25519 密钥:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ su hwchai                                              # 切换到 hwchai
$ ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 # 创建 Ed25519 密钥
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/id_ed25519
$ chmod 644 ~/.ssh/id_ed25519.pub # 设置正确的权限
$ cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys # 将公钥加入到自己的 authorized_keys 中
$ chmod 600 ~/.ssh/authorized_keys

$ su vi /etc/ssh/sshd_config # 回到 root 账号并编辑 ssh 配置文件
PubkeyAuthentication yes # 启用公钥认证
PasswordAuthentication no # 禁用密码登录(提高安全性)
PermitRootLogin prohibit-password # root 仅允许密钥登录

$ systemctl restart sshd

安装 Nvidia Driver, CUDA 及 cuDNN 环境

1
2
3
4
5
6
7
8
9
10
11
12
13
$ yum update -y
$ yum install pciutils # 使用 yum install pciutils 安装 lspci 工具
$ lspci | grep -i nvidia # 检查显卡连接状态
16:00.0 3D controller: NVIDIA Corporation GA100 [A100 PCIe 80GB] (rev a1)
34:00.0 3D controller: NVIDIA Corporation GA100 [A100 PCIe 80GB] (rev a1)
ac:00.0 3D controller: NVIDIA Corporation GA100 [A100 PCIe 80GB] (rev a1)
ca:00.0 3D controller: NVIDIA Corporation GA100 [A100 PCIe 80GB] (rev a1)

$ yum install -y gcc kernel-devel kernel-headers # 安装编译环境和内核开发包,确保与当前内核版本相匹配
$ yum install "kernel-devel-uname-r == $(uname -r)"

$ vi /etc/selinux/config # 修改SELINUX=disabled。保存退出
$ setenforce 0 # 临时关闭selinux

安装 Nvidia driver

禁用 Linux 默认的显示驱动 nouveau:

1
2
$ lsmod | grep nouveau          # 该命令若无输出,则跳过以下步骤
$ vi /lib/modprobe.d/dist-blacklist.conf

编辑 /lib/modprobe.d/dist-blacklist.conf 文件

1
2
3
# blacklist nvidiafb                 # 注释掉 blacklist nvidiafb
blacklist nouveau # 文件末尾添加:blacklist nouveau
options nouveau modeset=0 # 文件末尾添加:options nouveau modeset=0
1
2
3
4
5
6
$ mv /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.bak
$ dracut /boot/initramfs-$(uname -r).img $(uname -r) # 重建 initramfs image 镜像
$ systemctl set-default multi-user.target # 修改运行模式为文本模式

$ reboot
$ lsmod | grep nouveau # 该命令若无输出,表示禁用 nouveau 成功

Nvidia driver download根据GPU型号及操作系统选择相应的驱动程序下载至本地后安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ chmod +x NVIDIA-Linux-x86_64-版本号.run
$ ./NVIDIA-Linux-x86_64-版本号.run

$ nvidia-smi # 查看 Nvidia GPU 实时信息,验证 Nvidia driver
Thu Jun 5 22:19:43 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 570.133.20 Driver Version: 570.133.20 CUDA Version: 12.8 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA A100 80GB PCIe Off | 00000000:16:00.0 Off | 0 |
| N/A 28C P0 62W / 300W | 0MiB / 81920MiB | 0% Default |
| | | Disabled |

......

安装 CUDA Toolkit

CUDA Toolkit 12.9 Downloads中下载 CUDA 工具包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ wget https://developer.download.nvidia.com/compute/cuda/12.9.0/local_installers/cuda-repo-rhel9-12-9-local-12.9.0_575.51.03-1.x86_64.rpm
$ rpm -i cuda-repo-rhel9-12-9-local-12.9.0_575.51.03-1.x86_64.rpm
$ dnf clean all
$ dnf -y install cuda-toolkit-12-9

$ echo 'export PATH=/usr/local/cuda/bin:$PATH' >> ~/.bashrc
$ echo 'export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
$ source ~/.bashrc
$ nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2025 NVIDIA Corporation
Built on Wed_Apr__9_19:24:57_PDT_2025
Cuda compilation tools, release 12.9, V12.9.41
Build cuda_12.9.r12.9/compiler.35813241_0

安装 cuDNN

cuDNN 9.10.1 Downloads中下载 cuDNN 包:

1
2
3
4
$ wget https://developer.download.nvidia.com/compute/cudnn/9.10.1/local_installers/cudnn-local-repo-rhel9-9.10.1-1.0-1.x86_64.rpm
$ rpm -i cudnn-local-repo-rhel9-9.10.1-1.0-1.x86_64.rpm
$ dnf clean all
$ dnf -y install cudnn

安装Miniconda 3

1
2
3
4
5
6
7
$ curl -O https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
$ chmod +x Miniconda3-latest-Linux-x86_64.sh
$ ./Miniconda3-latest-Linux-x86_64.sh
$ source ~/.bashrc

$ conda --version
conda 24.4.0

安装 Pytorch 及 Transformers 等环境

1
2
3
$ conda create -n hwchai python=3.13        # 环境创建
$ conda activate hwchai # 激活环境
$ pip install torch transformers bitsandbytes tensorboard trl datasets peft

查看 Pytorch, CUDA 及 cuDNN 版本

1
2
3
4
5
6
7
8
>>> import torch
>>> print(torch.__version__)
... print(torch.version.cuda)
... print(torch.backends.cudnn.version())
...
2.7.1+cu126
12.6
90501

可能还有包未列出,可按照运行提示安装。

部署 Gemma 训练环境及预训练模型

注册Kaggle账号,在网站 https://www.kaggle.com/models/google/gemma 下载所需模型,配置要求:Python≥3.8,下载模型:

官方文档页面: https://github.com/google/gemma_pytorch ,文档中介绍了在Linux下使用docker配置环境并运行模型的方法,但未说明对模型进行调整的操作和对训练集的要求,使用常规Pytorch对NLP模型的训练方式即可。

下载的预训练模型分别存放在/home/ai/gemma-2b/home/ai/gemma-7b中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
(base) [root@server01 gemma-2b]# ls -al /home/ai/gemma-2b
total 14712812
drwxrwxr-x. 2 ai ai 4096 Jun 6 22:49 .
drwx------. 9 ai ai 4096 Jul 4 07:51 ..
-rw-rw-r--. 1 ai ai 634 Jun 6 22:49 config.json
-rw-rw-r--. 1 ai ai 10031780672 Jun 6 22:49 gemma-2b.gguf
-rw-rw-r--. 1 ai ai 137 Jun 6 22:18 generation_config.json
-rw-rw-r--. 1 ai ai 1620 Jun 6 22:49 .gitattributes
-rw-rw-r--. 1 ai ai 4945242264 Jun 6 22:18 model-00001-of-00002.safetensors
-rw-rw-r--. 1 ai ai 67121608 Jun 6 22:02 model-00002-of-00002.safetensors
-rw-rw-r--. 1 ai ai 13489 Jun 6 22:02 model.safetensors.index.json
-rw-rw-r--. 1 ai ai 555 Jun 6 22:02 special_tokens_map.json
-rw-rw-r--. 1 ai ai 1108 Jun 6 22:02 tokenizer_config.json
-rw-rw-r--. 1 ai ai 17477553 Jun 6 22:02 tokenizer.json
-rw-rw-r--. 1 ai ai 4241003 Jun 6 22:02 tokenizer.model
(base) [root@server01 gemma-2b]# ls -al /home/ai/gemma-7b
total 50054220
drwxrwxr-x 3 ai ai 4096 Jun 29 06:13 .
drwx------. 9 ai ai 4096 Jul 4 07:51 ..
-rw-rw-r-- 1 ai ai 636 Jun 29 06:13 config.json
drwxrwxr-x 2 ai ai 88 Jun 29 06:13 examples
-rw-rw-r-- 1 ai ai 34158344288 Jun 29 06:13 gemma-7b.gguf
-rw-rw-r-- 1 ai ai 137 Jun 29 05:25 generation_config.json
-rw-rw-r-- 1 ai ai 1620 Jun 29 06:13 .gitattributes
-rw-rw-r-- 1 ai ai 4995496656 Jun 29 05:25 model-00001-of-00004.safetensors
-rw-rw-r-- 1 ai ai 4982953168 Jun 29 05:18 model-00002-of-00004.safetensors
-rw-rw-r-- 1 ai ai 4982953200 Jun 29 05:11 model-00003-of-00004.safetensors
-rw-rw-r-- 1 ai ai 2113988336 Jun 29 05:04 model-00004-of-00004.safetensors
-rw-rw-r-- 1 ai ai 20920 Jun 29 05:01 model.safetensors.index.json
-rw-rw-r-- 1 ai ai 555 Jun 29 05:01 special_tokens_map.json
-rw-rw-r-- 1 ai ai 1108 Jun 29 05:01 tokenizer_config.json
-rw-rw-r-- 1 ai ai 17477553 Jun 29 05:01 tokenizer.json
-rw-rw-r-- 1 ai ai 4241003 Jun 29 05:01 tokenizer.model

测试数据集保存在另一路径,数据形如:

1
2
3
4
5
6
(base) [root@server01 ~]# less /dataset02/dataset/01.ont.seq/train/Exp001_shanzhu.aozhouhuangjin.raw.fastq.txt
TGCTTCGTTCAGTTACGTATTGCTAAGGTTAAACAGACGACTACAAACTGAATCGACAGCACCTCTTTCTATTATGGTGGACTTTATGTATTATAGTTTTGATTTGTGTATTATGGATTATGGTTGGTTGCTTTGATTTAGCTAGATTATGGATTACTTAGCCTCGTAAAGTGGTATCGATCGAAATGAGTGTAATGGTCGTGATG
ACATTTTGGAGGGTAACATCGATGTTTTGTTTAGATTGTAAAGAAGGGTGCCTATGGTATGTATGAGATGGGGTAAGAAGTGATTTTCTTGAATTGTCCATATTCCAATGTTTGGTTACTTAGTGAAATCGTCGGTGTTGATGCTTACTTGTTTTGTAGAATCATAATGGTGGCTAGC
TACTTCAGTTTCGGTTACGTATTGCTAAGGTTAACAGACGACTACAAAACGGAATCGACAGCACCTTTATTTTGTGTTTGTCGTTGGAGAATTGATCTTTCTTCAATGAAATTTATCTCTAGAATTTATTTGTTGATTAATTTCTAGGTTGAAGAACATAAAGAAATTCATAGATTAAATCCTATCTGAATAACTGGGGCCGATCT
ATGCGGCAATAAAAGGTTAATGATTTGTCTTTAATAAAGTTTATTTAAATCATGTATGATTAACCATGATCAATATAAATTTGGATAGGATTAATGTAATTTGATCGTAAGTACATTAATCAATCAAGATCACTATTTGGCTAGTAAAGGCAACAATTCAATTAGCATATCTATAGAAAATTGTCATATCATTACTTGGTTAAATT
······

Gemma模型导入与配置

编写脚本,使用transformers加载本地模型和分词器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import torch
from datasets import load_dataset
from peft import LoraConfig, PeftModel, prepare_model_for_kbit_training
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
AutoTokenizer,
TrainingArguments,
set_seed
)
from trl import SFTTrainer

model_path = "/data/models/gemma-2b-tf/"
set_seed(1234) # For reproducibility

model_path = "/data/models/gemma-2b-tf/"
set_seed(1234) # For reproducibility

# Tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_path, add_eos_token=True, use_fast=True)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.pad_token_id = tokenizer.eos_token_id
tokenizer.padding_side = 'left'

data_files = {"train": "/data/datasets/WNLI/train1.tsv", "test": "/data/datasets/WNLI/dev1.tsv"}
ds = load_dataset("csv", data_files=data_files, delimiter="\t")

compute_dtype = getattr(torch, "float16")
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=compute_dtype,
bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained(
model_path, quantization_config=bnb_config, device_map={"": 0}
)
model = prepare_model_for_kbit_training(model)
# Configure the pad token in the model
model.config.pad_token_id = tokenizer.pad_token_id
model.config.use_cache = False
# 编辑微调配置
peft_config = LoraConfig(
lora_alpha=16,
lora_dropout=0.05,
r=16,
bias="none",
task_type="CAUSAL_LM",
target_modules= ['k_proj', 'q_proj', 'v_proj', 'o_proj', "gate_proj", "down_proj", "up_proj"]
)
# 设定训练参数
training_arguments = TrainingArguments(
output_dir="./results_qlora",
evaluation_strategy="steps",
do_eval=True,
optim="paged_adamw_8bit",
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
log_level="debug",
save_steps=50,
logging_steps=50,
learning_rate=2e-5,
eval_steps=50,
max_steps=300,
warmup_steps=30,
lr_scheduler_type="linear",
)
# 载入训练配置
trainer = SFTTrainer(
model=model,
train_dataset=ds['train'],
eval_dataset=ds['test'],
peft_config=peft_config,
dataset_text_field="text",
max_seq_length=128,
tokenizer=tokenizer,
args=training_arguments,
)
#开始训练
trainer.train()

将脚本保存为gemmatrain.py,命令行运行python gemma.train.py ,即可运行。

在上述脚本中导入了tsv格式的文本作为训练和测试集,实际上原始格式的文本并不能直接作为模型的输入,还需将一整行的文本内容(即单个输入输出对)记在同一标签“text”下。如:

1
2
3
4
text
sentence1:xxxxxxxx.senstence2:xxxxxxxx.label:n
sentence1:xxxxxxxx.senstence2:xxxxxxxx.label:n
sentence1:xxxxxxxx.senstence2:xxxxxxxx.label:n

以上格式仅为测试使用,后续会考虑数据集的具体内容和训练需求进行优化。