1. 賽題
參賽者需要構建端到端的評論分析系統,完成三大核心任務:
- 商品識別
- 輸入:視頻描述文本(video_desc)和標簽(video_tags)
- 輸出:精準識別推廣商品名稱(Xfaiyx Smart Translator/Recorder)
- 多維情感分析
- 維度:情感傾向(5類)、用戶場景、用戶疑問、用戶建議
- 挑戰:處理隱晦表達如"這重量出門帶著剛好"(場景暗示)
- 評論聚類與主題提煉
- 對5類特定評論進行聚類分析
- 生成5-8個具有代表性的主題詞
由于這里只有很少量的數據是有標簽的(商品識別中總共86個數據,大約30個有label;評論總共6400+數據,大約1700+有label),而且不同類別的分布數目相差很大,因此很明顯不能從頭訓練模型。如果選用傳統的DNN,如Seq2Seq等,即使微調也需要足夠的數據,沒有能力接收少量且差異化特別大的數據就能夠進行分類。
因此,我們需要使用原本就具有所需的分類能力的模型——LLM
2. 商品識別——SFT 微調
主要流程為:
- 下載模型權重checkpoint
- 處理數據集dataset
- 微調LLM
- 在測試集上運行
2.1 準備第三方工具
這里作者使用了算力平臺,有條件的可以本地部署,也可以直接調用大模型的API
2.1.1 安裝 LLaMA-Factory
LLaMA-Factory可以很方便的進行Pre-Training, SFT, PPO, DPO等
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]" --no-build-isolation
2.1.2 安裝 huggingface_hub
后續checkpoint都需要從huggingface中下載,相比較本地下載再upload到服務器上,直接從官網下載會方便很多
pip install -U huggingface_hub
直接從官網下載網速會有點慢,可以使用國內鏡像
vim ~/.bashrc# vim下修改文件, 增加下方內容
export HF_ENDPOINT="https://hf-mirror.com"
# 保存并退出source ~/.bashrc
2.2 下載模型權重
下載過程由于耗時較長,為了防止ssh關閉導致下載終止,可以使用screen開個后臺
選擇參數量較小的Qwen3-1.7B進行微調,在單卡下微調時長約為3分鐘
huggingface-cli download --resume-download Qwen/Qwen3-1.7B --local-dir models/qwen3-1.7B
2.3 處理數據
大致思路為:
- 將video_desc與video_tag合并,作為模型輸入
- 添加instruction作為prompt
- 選擇有label的部分,作為訓練集
那么直接貼代碼:
import argparse
import pandas as pd
from transformers import AutoTokenizer, AutoModelForCausalLMvideo_data = pd.read_csv("origin_videos_data.csv")
video_data["text"] = video_data["video_desc"].fillna("") + " " + video_data["video_tags"].fillna("")def preprocess_data():text_data = video_data[["text", "product_name"]]text_data = text_data[text_data["product_name"].notnull()]instruction = '''你是一個專業的電商產品識別系統。請從視頻描述中識別推廣商品,必須嚴格選擇以下選項之一:- Xfaiyx Smart Translator- Xfaiyx Smart Recorder規則說明:1. 當描述提到"翻譯"、"多語言"等關鍵詞時選擇Translator2. 當描述出現"錄音"、"轉寫"等關鍵詞時選擇Recorder3. 遇到不確定情況時選擇更符合主要功能的選項示例:輸入:這款設備支持實時語音轉文字輸出:Xfaiyx Smart Recorder'''text_data["instruction"] = instruction.strip()text_data.to_json("text_data.json", orient="records", force_ascii=False)if __name__ == "__main__":parser = argparse.ArgumentParser()parser.add_argument("--preprocess", type=bool, default=False)parser.add_argument("--device", type=str, default="cuda")args = parser.parse_args()if args.preprocess:preprocess_data()
運行后數據變為:
2.4 微調模型
2.4.1 修改data_info
在LLaMA-Factory/data/dataset_info.json中增加自定義數據集
"dw_data_video": {"file_name": "DW/text_data.json","formatting": "alpaca","spilt": "train","columns": {"query": "text","response": "product_name","prompt": "instruction"}},
參數的具體含義如下:
- file_name : 數據集的路徑
- formatting : 數據集的格式,主流LLM訓練數據的格式分為alpaca和sharegpt
- split : 將數據集劃分為train, test, val,默認僅作為train
- columns : 指定哪個key作為模型接收的輸入/輸出
原本模型是以"input" : “message"作為模型接收到的輸入,但dw_data_video中模型輸入為"text” : “message”,因此需要修改
2.4.2 修改微調配置
設置sft配置文件,文件路徑隨意,但在后續訓練啟動命令時,需要指定文件路徑
### model
model_name_or_path: models/qwen3-1.7B # 下載預訓練模型的路徑
### method
stage: sft
do_train: true
finetuning_type: lora #微調方式
lora_target: all### dataset
dataset: dw_data_video
template: qwen3 # 注意:這里的參數選項要嚴格與官方給出的表格一致
cutoff_len: 2048
max_samples: 1000
overwrite_cache: true
preprocessing_num_workers: 16### output
output_dir: output/sft/lora/qwen3-1.7B-dw-data-video
logging_steps: 10
save_steps: 500
plot_loss: true
overwrite_output_dir: true### train
per_device_train_batch_size: 16
gradient_accumulation_steps: 8
learning_rate: 1.0e-4
num_train_epochs: 10
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000
run_name: qwen3-1.7B-dw-data-video # 用于wandb展示過程### eval
val_size: 0.1
per_device_eval_batch_size: 1
eval_strategy: steps
eval_steps: 500
2.4.3. 啟動訓練
注意文件路徑需要與上面創建文件時的路徑一致
llamafactory-cli train LLaMA-Factory/workspace/lora/sft/sft_lora_qwen3.yaml
官方給出的代碼為llamafactory-cli train xxxx.yaml,但由于我們的yaml文件存儲路徑不一定在官方默認路徑下,因此使用完整路徑更穩當
注意:啟動時,pwd一定要在LLaMA-Factory文件夾下,不然會報錯No such file data/dataset_info.json
2.5 運行測試集數據
import argparse
import pandas as pd
from transformers import AutoTokenizer, AutoModelForCausalLMvideo_data = pd.read_csv("origin_videos_data.csv")
video_data["text"] = video_data["video_desc"].fillna("") + " " + video_data["video_tags"].fillna("")def preprocess_data():text_data = video_data[["text", "product_name"]]text_data = text_data[text_data["product_name"].notnull()]instruction = '''你是一個專業的電商產品識別系統。請從視頻描述中識別推廣商品,必須嚴格選擇以下選項之一:- Xfaiyx Smart Translator- Xfaiyx Smart Recorder規則說明:1. 當描述提到"翻譯"、"多語言"等關鍵詞時選擇Translator2. 當描述出現"錄音"、"轉寫"等關鍵詞時選擇Recorder3. 遇到不確定情況時選擇更符合主要功能的選項示例:輸入:這款設備支持實時語音轉文字輸出:Xfaiyx Smart Recorder'''text_data["instruction"] = instruction.strip()text_data.to_json("text_data.json", orient="records", force_ascii=False)# 定義推理函數
def predict_product(text, device):instruction = ("你是一個專業的電商產品識別系統。請從視頻描述中識別推廣商品,必須嚴格選擇以下選項之一:\n"" - Xfaiyx Smart Translator\n"" - Xfaiyx Smart Recorder\n\n""規則說明:\n""1. 當描述提到\"翻譯\"、\"多語言\"等關鍵詞時選擇Translator\n""2. 當描述出現\"錄音\"、\"轉寫\"等關鍵詞時選擇Recorder\n""3. 遇到不確定情況時選擇更符合主要功能的選項")input_text = f"{instruction}\n\n輸入:{text}\n輸出:"inputs = tokenizer(input_text, return_tensors="pt").to(device)outputs = model.generate(inputs.input_ids,max_new_tokens=20, # 限制生成長度num_beams=1, # 使用貪婪搜索do_sample=False, # 禁用隨機采樣pad_token_id=tokenizer.eos_token_id # 設置填充token)full_output = tokenizer.decode(outputs[0], skip_special_tokens=True)generated_output = full_output[len(input_text):] # 提取生成部分# 清理輸出結果return generated_output.strip().split("\n")[0]if __name__ == "__main__":parser = argparse.ArgumentParser()parser.add_argument("--preprocess", type=bool, default=False)parser.add_argument("--device", type=str, default="cuda")args = parser.parse_args()if args.preprocess:preprocess_data()device = args.devicemodel_path = 'output/sft/lora/qwen3-1.7B-DWData'tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True).to(device)# 遍歷csv,推理并保存結果for index, row in video_data.iterrows():# 如果product_name列非空則保存product_nameif pd.notna(row["product_name"]):video_data.at[index, "prediction"] = row["product_name"]continuetext = row["text"]prediction = predict_product(text, device)video_data.at[index, "prediction"] = prediction# 用prediction列覆蓋product_name列video_data["product_name"] = video_data["prediction"]# 只保存 video_id 和 product_name 列video_data = video_data[["video_id", "product_name"]]video_data.to_csv("video_data_with_predictions.csv", index=False)
那么此時輸出的文件就是預測后的文件
將輸出的文件和Baseline的文件上傳,發現分數提高了10分左右,目前來到了180分,說明LLM微調后效果還不錯
后續內容努力碼字中