1.資料部分
CodeGeeX 基於 ChatGLM 基座語言模型,所以需要關注 ChatGLM 的訓練資料。
訓練資料為jsonl格式,每一行的資料格式如下,其中chat_rounds欄位是必需的,可以根據實際需求新增或刪除其他欄位。
在本專案當中,我們更加關注的是模型的單輪對話能力,所以只需要單輪對話資料。
推理資料格式為模型在訓練資料格式下拼接的字串形式,它也是推理時輸入prompt拼接的方式:
<s>system
這是System指令
<s>human
這是第1輪使用者輸入的問題
<s>bot
這是第1輪模型生成的內容{EOS_TOKEN}
<s>human
這是第2輪使用者輸入的問題
<s>bot
這是第2輪模型生成的內容{EOS_TOKEN}
...
...
...
<s>human
這是第n輪使用者輸入的問題
<s>bot
{模型現在要生成的內容}{EOS_TOKEN}
將資料處理成以上的格式:
還需要知道 {EOS_TOKEN} 的值,可以直接透過 tokenizer 的 eos_token 屬性檢視:
得知 {EOS_TOKEN} 為 </s>
,將資料進一步進行處理:
最終資料為
'<s>human\nYou are an intelligent code assistant who can respond to users\' questions related to codeThere are $n$ candy boxes in front of Tania. The boxes are arranged in a row from left to right, numbered from $1$ to $n$. The $i$-th box contains $r_i$ candies, candies have the color $c_i$ (the color can take one of three values \u200b\u200b— red,green, or blue). All candies inside a single box have the same color (and it is equal to $c_i$).\n\nInitially, Tanya is next to the box number $s$. Tanya can move to the neighbor box (that is, with a number that differs by one) or eat candies in the current box. Tanya eats candies instantly, but the movement takes one second.\n\nIf Tanya eats candies from the box, then the box itself remains in place, but there is no more candies in it. In other words, Tanya always eats all the candies from the box and candies in the boxes are not refilled.\n\nIt is known that Tanya cannot eat candies of the same color one after another (that is, the colors of candies in two consecutive boxes from which she eats candies are always different). In addition, Tanya\'s appetite is constantly growing, so in each next box from which she eats candies, there should be strictly more candies than in the previous one.\n\nNote that for the first box from which Tanya will eat candies, there are no restrictions on the color and number of candies.\n\nTanya wants to eat at least $k$ candies. What is the minimum number of seconds she will need? Remember that she eats candies instantly, and time is spent only on movements.\n\n\n-----Input-----\n\nThe first line contains three integers $n$, $s$ and $k$ ($1 \\le n \\le 50$, $1 \\le s \\le n$, $1 \\le k \\le 2000$) — number of the boxes, initial position of Tanya and lower bound on number of candies to eat. The following line contains $n$ integers $r_i$ ($1 \\le r_i \\le 50$) — numbers of candies in the boxes. The third line contains sequence of $n$ letters \'R\', \'G\' and \'B\', meaning the colors of candies in the correspondent boxes (\'R\' for red, \'G\' for green, \'B\' for blue). Recall that each box contains candies of only one color. The third line contains no spaces.\n\n\n-----Output-----\n\nPrint minimal number of seconds to eat at least $k$ candies. If solution doesn\'t exist, print "-1".\n\n\n-----Examples-----\nInput\n5 3 10\n1 2 3 4 5\nRGBRR\n\nOutput\n4\n\nInput\n2 1 15\n5 6\nRG\n\nOutput\n-1\n\n\n\n-----Note-----\n\nThe sequence of actions of Tanya for the first example:\n\n move from the box $3$ to the box $2$; eat candies from the box $2$; move from the box $2$ to the box $3$; eat candy from the box $3$; move from the box $3$ to the box $4$; move from the box $4$ to the box $5$; eat candies from the box $5$. \n\nSince Tanya eats candy instantly, the required time is four seconds.<s>bot\nINF = 10000000000.0\nmax_n = 50\nmax_k = 2000\n\ndef main():\n\t(n, s, k) = map(int, input().split())\n\ts -= 1\n\tbuf = [\'\'] * (max_n + 1)\n\tdp = [[0 for i in range(max_n + 1)] for j in range(max_k + 1)]\n\tr = list(map(int, input().split()))\n\tc = input()\n\tanswer = INF\n\tfor i in range(len(c)):\n\t\tbuf[i] = c[i]\n\tfor i in range(k, -1, -1):\n\t\tfor j in range(n):\n\t\t\tdp[i][j] = INF\n\tfor j in range(n):\n\t\tvalue = abs(j - s)\n\t\tif k - r[j] <= 0:\n\t\t\tanswer = min(answer, value)\n\t\telse:\n\t\t\tdp[k - r[j]][j] = value\n\tfor i in range(k, 0, -1):\n\t\tfor j in range(n):\n\t\t\tif dp[i][j] < INF:\n\t\t\t\tfor l in range(n):\n\t\t\t\t\tif buf[j] != buf[l] and r[j] < r[l]:\n\t\t\t\t\t\tvalue = dp[i][j] + abs(j - l)\n\t\t\t\t\t\tif i - r[l] <= 0:\n\t\t\t\t\t\t\tanswer = min(answer, value)\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\tdp[i - r[l]][l] = min(dp[i - r[l]][l], value)\n\tif answer == INF:\n\t\tprint(-1)\n\t\treturn\n\tprint(answer)\n\ndef __starting_point():\n\tmain()\n__starting_point()\n</s>'
2.模型載入
使用 https://huggingface.co/THUDM/codegeex2-6b 中的方法,對模型進行量化載入。
載入之後可以顯示一些模型的結構:
我們使用模型微調的主要會是模型中的線性層,我們從中選取出要增加 LoRa 引數的層。
輸出的層即為要新增引數的層,均為線性層。
3.使用 PEFT 微調
載入 PEFT 配置,每個 PEFT 方法都需要一個配置,其中包含了指定 PEFT 方法應該如何應用的所有引數。一旦配置設定好了,就將其傳遞給 get_peft_model() 函式,同時還要傳遞基礎模型,以建立一個可訓練的 PeftModel。
從 peft 庫中匯入 LoraConfig 和 get_peft_model 函式,設定 LoRa 微調的一些超引數 (lora_alpha: LORA 介面卡的比例因子;lora_dropout: LORA 介面卡的dropout率;lora_r: LORA 介面卡的秩)。"CAUSAL_LM",表示這是一個因果語言模型微調任務。
使用 Transformers 庫和 TRL 庫來進行 SFT 訓練,即是有監督的學習,開啟了混合精度訓練加速訓練。
- out_dir: 模型儲存的輸出目錄
- per_device_train_batch_size: 每個裝置的訓練批次大小
- gradient_accumulation_steps: 梯度累積步數
- warmup_steps: 學習率預熱步數
- max_steps: 最大訓練步數
- learning_rate: 初始學習率
- logging_steps: 多少步列印一次日誌
對模型進行訓練,會得到訓練過程中的 Loss 變化: