import json
import os
import time
import re
from typing import List, Dict
from openai import OpenAI
import logging
import backoff
import pyarrow as pa
import pyarrow.parquet as pq
# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# 初始化 OpenAI 客户端
client = OpenAI(base_url="https://api.together.xyz/v1", api_key="your_api_Key") # 替换为你的 API 密钥
def read_file(file_path: str) -> str:
with open(file_path, 'r', encoding='utf-8') as file:
return file.read()
@backoff.on_exception(backoff.expo, Exception, max_tries=3)
def generate_single_entry(text: str) -> Dict:
prompt = f"""
基于以下文本,生成1个用于指令数据集的高质量条目。条目应该直接关联到给定的文本内容,提出相关的问题或任务。
请确保生成多样化的指令类型,例如:
- 分析类:"分析..."
- 比较类:"比较..."
- 解释类:"解释..."
- 评价类:"评价..."
- 问答类:"为什么..."
文本内容:
{text}
请以下面的格式生成条目,确保所有字段都有适当的内容:
{{
"instruction": "使用上述多样化的指令类型之一,提出一个具体的、与文本相关的问题或任务",
"input": "如果需要额外的上下文信息,请在这里提供,否则留空",
"output": "对instruction的详细回答或任务的完成结果"
}}
确保所有生成的内容都与给定的文本直接相关,生成的是有效的JSON格式,并且内容高质量、准确、详细。
"""
try:
response = client.chat.completions.create(
model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
messages=[{"role": "user", "content": prompt}],
temperature=0.7, # 增加温度以提高多样性
max_tokens=4098
)
logger.info(f"API 响应: {response.choices[0].message.content}")
json_match = re.search(r'\{.*\}', response.choices[0].message.content, re.DOTALL)
if json_match:
entry = json.loads(json_match.group())
required_keys = ['instruction', 'input', 'output']
if isinstance(entry, dict) and all(key in entry for key in required_keys):
# 根据 input 是否为空来设置 text 字段
if entry['input'].strip():
entry[
'text'] = f"Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.### Instruction: {entry['instruction']}\n### Input: {entry['input']}\n### Response: {entry['output']}"
else:
entry[
'text'] = f"Below is an instruction that describes a task. Write a response that appropriately completes the request.### Instruction: {entry['instruction']}\n### Input: {entry['input']}\n### Response: {entry['output']}"
logger.info("成功生成完整条目")
return entry
else:
logger.warning("JSON 解析成功,但缺少必要字段")
return {}
else:
logger.error("无法从API响应中提取有效的JSON")
return {}
except Exception as e:
logger.error(f"生成条目时发生错误: {str(e)}")
raise
def generate_dataset(folder_path: str, entries_per_file: int = 2) -> List[Dict]:
dataset = []
for filename in os.listdir(folder_path):
if filename.endswith(".txt"):
file_path = os.path.join(folder_path, filename)
logger.info(f"正在处理文件: {filename}")
text = read_file(file_path)
for j in range(entries_per_file):
logger.info(f" 生成第 {j + 1}/{entries_per_file} 个条目")
entry = generate_single_entry(text)
if entry and all(key in entry for key in ['instruction', 'input', 'output', 'text']):
dataset.append(entry)
logger.info(f" 成功生成 1 个完整条目")
else:
logger.warning(f" 跳过不完整的条目")
time.sleep(2) # 在请求之间增加延迟到2秒
return dataset
def save_dataset_as_parquet(dataset: List[Dict], output_file: str):
schema = pa.schema([
('instruction', pa.string()),
('input', pa.string()),
('output', pa.string()),
('text', pa.string())
])
arrays = [
pa.array([entry['instruction'] for entry in dataset]),
pa.array([entry['input'] for entry in dataset]),
pa.array([entry['output'] for entry in dataset]),
pa.array([entry['text'] for entry in dataset])
]
table = pa.Table.from_arrays(arrays, schema=schema)
pq.write_table(table, output_file)
if __name__ == "__main__":
input_folder = "./saveChunk" # 指定输入文件夹路径
output_file = "instruction_dataset.parquet"
logger.info("开始生成数据集")
dataset = generate_dataset(input_folder, entries_per_file=10)
save_dataset_as_parquet(dataset, output_file)
logger.info(f"数据集已生成并保存到 {output_file}")
logger.info(f"共生成 {len(dataset)} 个有效条目")
微调命令
#将当前登录的用户添加到docker组中,使其拥有使用docker命令的权限。
sudo usermod -aG docker $USER
#sudo docker run --gpus '"all"' -it winglian/axolotl:main-latest
sudo docker run --gpus '"all"' --rm -it winglian/axolotl:main-latest
rm examples/llama-3/qlora.yml
wget -P examples/llama-3/ https://raw.githubusercontent.com/win4r/mytest/main/qlora.yml
#数据集 https://raw.githubusercontent.com/win4r/mytest/main/qlora.yml
#模型 NousResearch/Meta-Llama-3.1-8B
#数据集 leo009/lawdata
CUDA_VISIBLE_DEVICES="" python -m axolotl.cli.preprocess examples/llama-3/qlora.yml
accelerate launch -m axolotl.cli.train examples/llama-3/qlora.yml
# 预处理数据集
CUDA_VISIBLE_DEVICES="" python -m axolotl.cli.preprocess examples/llama-3/instruct-dpo-lora-8b.yml
# finetune lora
accelerate launch -m axolotl.cli.train examples/llama-3/instruct-dpo-lora-8b.yml
# inference
accelerate launch -m axolotl.cli.inference examples/llama-3/qlora.yml \
--lora_model_dir="./outputs/qlora-out"
# gradio
accelerate launch -m axolotl.cli.inference examples/llama-3/qlora.yml \
--lora_model_dir="./outputs/qlora-out" --gradio
# remote yaml files - the yaml config can be hosted on a public URL
# Note: the yaml config must directly link to the **raw** yaml
accelerate launch -m axolotl.cli.train https://raw.githubusercontent.com/axolotl-ai-cloud/axolotl/main/examples/openllama-3b/lora.yml
#合并模型
python3 -m axolotl.cli.merge_lora examples/llama-3/qlora.yml --lora_model_dir="./outputs/qlora-out"
#上次合并后到模型到hf
huggingface-cli login
#然后输入write权限的token
huggingface-cli upload leo009/merged-llama3.1-8b outputs/qlora-out/merged
数据集处理
#pip install ollama datasets
import json
from datasets import load_dataset
import ollama
from rich import print
# Load a real dataset from Hugging Face
dataset = load_dataset("squad_v2", split="train")
# Convert dataset to list of dictionaries
json_data = dataset.select(range(10)).to_dict()
print("Dataset Rows:", len(json_data['id']))
print({key: json_data[key][0] for key in json_data})
# Function to format the input for the model
def format_input(context, question):
return (
"### Context:\n" + context +
("\n\n### Question:\n" + question if question else "")
)
# Initialize the Ollama client
client = ollama.Client() # Replace with your actual API key if required
# Initialize new keys in the json_data dictionary
json_data['rejected'] = [''] * len(json_data['id'])
json_data['chosen'] = [''] * len(json_data['id'])
# Process each entry in the dataset
for i in range(len(json_data['id'])):
context = json_data['context'][i]
question = json_data['question'][i]
answer = json_data['answers'][i]['text'][0] if json_data['answers'][i]['text'] else "No answer"
print("Rejected Answer:", answer)
prompt_text = format_input(context, question)
prompt = (
f"Rewrite `{prompt_text}` output to be concise and clear: {answer}. "
"Ensure the response is easy to understand, professional and as a full sentense. Just respond only with the Answer"
)
response = client.generate(
model="llama3.1",
prompt=prompt,
options={"seed": 123, "temperature": 0}
)
chosen_answer = response['response']
print("Chosen Answer:", chosen_answer)
json_data['rejected'][i] = answer
json_data['chosen'][i] = chosen_answer
# Convert back to dictionary format expected by json.dump
final_data = [{key: json_data[key][i] for key in json_data} for i in range(len(json_data['id']))]
with open("preference_dataset.json", "w") as file:
json.dump(final_data, file, indent=4)
转alpaca数据集
import json
from datasets import load_dataset
import ollama
from rich import print
# Load a real dataset from Hugging Face
dataset = load_dataset("squad_v2", split="train")
# Convert dataset to list of dictionaries
json_data = dataset.select(range(10)).to_dict()
print("Dataset Rows:", len(json_data['id']))
print({key: json_data[key][0] for key in json_data})
# Function to format the input for the model
def format_input(context, question):
return (
"### Context:\n" + context +
("\n\n### Question:\n" + question if question else "")
)
# Initialize the Ollama client
client = ollama.Client() # Replace with your actual API key if required
# Initialize list to store Alpaca format data
alpaca_data = []
# Process each entry in the dataset
for i in range(len(json_data['id'])):
context = json_data['context'][i]
question = json_data['question'][i]
original_answer = json_data['answers'][i]['text'][0] if json_data['answers'][i]['text'] else "No answer"
print("Original Answer:", original_answer)
prompt_text = format_input(context, question)
prompt = (
f"Rewrite `{prompt_text}` output to be concise and clear: {original_answer}. "
"Ensure the response is easy to understand, professional and as a full sentence. Just respond only with the Answer"
)
response = client.generate(
model="llama3.1",
prompt=prompt,
options={"seed": 123, "temperature": 0}
)
improved_answer = response['response']
print("Improved Answer:", improved_answer)
# Create Alpaca format entry
alpaca_entry = {
"instruction": "Answer the following question based on the given context.",
"input": f"Context: {context}\n\nQuestion: {question}",
"output": improved_answer
}
alpaca_data.append(alpaca_entry)
# Save in Alpaca format
with open("alpaca_dataset.json", "w") as file:
json.dump(alpaca_data, file, indent=4)
print("Alpaca format dataset saved as 'alpaca_dataset.json'")