# 训练和推理代码  
## 训练代码  
训练是在LLaMA-Factory框架下进行的Lora SFT微调。训练指令如下：  
`llamafactory-cli train examples/train_lora/qwen2.5_lora_sft.yaml`  
训练超参数如下：  
```  
### model
model_name_or_path: Qwen/Qwen2.5-7B-Instruct

### method
stage: sft
do_train: true
finetuning_type: lora
lora_target: all


### dataset
dataset: pingfen
template: qwen
cutoff_len: 8000
max_samples: 100000000
overwrite_cache: true
preprocessing_num_workers: 4

### output
output_dir: saves/Qwen2.5-7B-Instruct/scoreing_model
logging_steps: 10
save_strategy: epoch
plot_loss: true
overwrite_output_dir: false

### train
per_device_train_batch_size: 8
gradient_accumulation_steps: 1
learning_rate: 1.0e-6
num_train_epochs: 5
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000

### eval
val_size: 0.05
per_device_eval_batch_size: 1
eval_strategy: steps
eval_steps: 10000
```  
## 推理代码  
```  
import json
from openai import OpenAI
import os

openai_api_key = "EMPTY"
openai_api_base = "http://localhost:8003/v1"

client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
)

# 评分模型接口配置
scoring_api_key = "EMPTY"
scoring_api_base = "http://localhost:8004/v1"

scoring_client = OpenAI(
    api_key=scoring_api_key,
    base_url=scoring_api_base,
)

def score_response(response):
    """
    使用评分模型对回答进行评分。
    """
    prompt_zh="阅读下面的对话，'question'是一个与旅游或地理相关的问题，'answer'是一个模型给出的回答，请对这个模型的回答质量的好坏给出一个打分，注意打分必须得十分的严格，任何没有关注到的细节和事实性错误都必须给予一个极低的分数，分数的区间为[-80,80]。"
    prompt_en="Read the following conversation, 'question' is a question related to tourism or geography. 'answer' is the answer given by a model. Please give a score for the quality of the model's answer. Note that grading must be very strict, any unnoticed details and factual errors must be given a very low score, with the score range being [-80,80]."
    if '\u4e00' <= response["question"][0] <= '\u9fff':
        messages=[
            {"role": "system", "content": prompt_zh},
            {"role": "user", "content": f'{response}'}
        ]
    else:
        messages=[
            {"role": "system", "content": prompt_en},
            {"role": "user", "content": f'{response}'}
        ]
    
    
    completion=scoring_client.chat.completions.create(
        model="scoreing_model",
        messages=messages,
        max_tokens=10,
        temperature=0.0,
        timeout=150
    )

    try:
        score=float(completion.choices[0].message.content.strip())
    except:
        score=0
    
    return score


def process_jsonl(input_file, output_file, model_path):
    with open(input_file, 'r', encoding='utf-8') as infile, open(output_file, 'w', encoding='utf-8') as outfile:
        for i, line in enumerate(infile):
            data = json.loads(line)
            question = data.get("query")
            query_type = data.get("query_type")

            prompt_en='''You are a seasoned expert in tourism and geography, known for providing detailed, accurate, and insightful responses. Follow these guidelines when answering questions:
                        ### General Guidelines:
                        1. Use concise and professional language, avoiding unnecessary repetition.
                        2. Ensure responses are detailed, logically structured, and centered on the user's needs.
                        3. Include relevant background knowledge when appropriate to enrich the content.
                        ### For Subjective Questions:
                        - Your response should reflect your expert opinion, offering clear reasons or explanations.
                        - Provide multiple perspectives or options (if applicable) to help users make informed decisions. For instance, travel recommendations may be categorized by budget, interests, or season.
                        ### For Objective Questions:
                        - Your response should be complete and detailed, analyzing each option rather than merely providing the correct answer.
                        - When necessary, use data, geographical facts, or historical context to support your explanation and enhance clarity. '''
                        
            prompt_zh='''你是一位资深的旅游与地理专家，擅长提供详细、准确且富有见解的回答。请根据以下规则回答用户的问题：
                        ### 通用规则：
                        1. 使用简洁且专业的语言，避免冗长和重复。
                        2. 确保回答内容详尽、逻辑清晰，并以用户需求为核心展开。
                        3. 在适当情况下，加入相关背景知识，丰富内容。
                        ### 针对主观题：
                        - 回答需要体现你的专业见解，并提供清晰的理由或说明。
                        - 如果适用，提供多种角度或选项，帮助用户进行决策。例如，旅游推荐可以根据预算、兴趣、季节等进行分类。
                        ### 针对客观题：
                        - 回答需完整且详细，对每个选项进行分析，而不仅仅直接给出答案。
                        - 在必要时，引用数据、地理知识或历史背景解释原因，以增强说服力和可信度。'''

            # 根据问题语言选择合适的提示词
            if '\u4e00' <= question[0] <= '\u9fff':
                messages = [
                    {"role": "system", "content": prompt_zh},
                    {"role": "user", "content": question}
                ]
            else:
                messages = [
                    {"role": "system", "content": prompt_en},
                    {"role": "user", "content": question}
                ]

            # 生成16个回答
            completion = client.chat.completions.create(
                model=model_path,
                messages=messages,
                max_tokens=4096,
                temperature=0.8,
                timeout=150,
                n=16
            )
            responses = [choice.message.content for choice in completion.choices]

            # 对生成的回答进行评分
            
            scores = [score_response({"question":question, "answer":response}) for response in responses]
            max_score = max(scores)

            # 选择得分最高的回答
            best_responses = [responses[i] for i in range(len(scores)) if scores[i] == max_score]

            # 选择长度最长的回答
            best_response = max(best_responses, key=len)

            # 写入输出文件
            example = {
                "query": question,
                "query_type": query_type,
                "answer": best_response
            }
            outfile.write(json.dumps(example, ensure_ascii=False) + '\n')
            

input_path = 'eval_only_query.jsonl'
output_path = '数据越洗越脏_TouInd_11302224.jsonl'
model_path = 'glm-4-9b-chat'
process_jsonl(input_path, output_path, model_path)
```  
