This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ckpt_dir": "/mnt/workspace/yzhao/modelscope/swift/output/pai_test/checkpoint-6",
|
||||
"val_dataset_sample": 2,
|
||||
"load_dataset_config": true
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"model_type": "qwen-1_8b-chat",
|
||||
"dataset": "jd-sentiment-zh",
|
||||
"output_dir": "output/pai_test",
|
||||
"train_dataset_sample": 100,
|
||||
"eval_steps": 5
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
system,instruction,input,output
|
||||
00000,11111,22222,3.3
|
||||
,aaaaa,,ccccc
|
||||
,AAAAA,BBBBB,CCCCC
|
||||
|
@@ -0,0 +1,3 @@
|
||||
{"instruction": "11111", "input": "22222", "output": "33333", "history": [["aaaaa", "bbbbb"]], "system": "system123"}
|
||||
{"instruction": "aaaaa", "output": "ccccc"}
|
||||
{"instruction": "AAAAA", "input": "BBBBB", "output": "CCCCC"}
|
||||
@@ -0,0 +1,4 @@
|
||||
instruction,output
|
||||
11111,33333
|
||||
aaaaa,ccccc
|
||||
AAAAA,CCCCC
|
||||
|
@@ -0,0 +1,3 @@
|
||||
{"messages": [{"role": "system", "content": "00000"}, {"role": "user", "content": "11111"}, {"role": "assistant", "content": "22222"}]}
|
||||
{"messages": [{"role": "user", "content": "aaaaa"}, {"role": "assistant", "content": "bbbbb"}, {"role": "user", "content": "ccccc"}, {"role": "assistant", "content": "ddddd"}]}
|
||||
{"messages": [{"role": "user", "content": "AAAAA"}, {"role": "assistant", "content": "BBBBB"}, {"role": "user", "content": "CCCCC"}, {"role": "assistant", "content": "DDDDD"}]}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"conversations": [{"from": "system", "value": "00000"}, {"from": "user", "value": "11111"}, {"from": "assistant", "value": "22222"}]}
|
||||
{"conversations": [{"from": "user", "value": "aaaaa"}, {"from": "assistant", "value": "bbbbb"}, {"from": "user", "value": "ccccc"}, {"from": "assistant", "value": "ddddd"}]}
|
||||
{"conversations": [{"from": "user", "value": "AAAAA"}, {"from": "assistant", "value": "BBBBB"}, {"from": "user", "value": "CCCCC"}, {"from": "assistant", "value": "DDDDD"}]}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"query": "<img>https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg</img>55555", "response": "66666"}
|
||||
{"query": "<img>https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg</img><img>https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg</img>eeeee", "response": "fffff", "history": [["hello", "123"]]}
|
||||
{"query": "EEEEE", "response": "FFFFF", "history": [["AAAAA", "BBBBB"], ["CCCCC", "DDDDD"]]}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"query": "55555", "response": "66666", "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
|
||||
{"query": "eeeee", "response": "fffff", "history": [], "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
|
||||
{"query": "EEEEE", "response": "FFFFF", "history": [["AAAAA", "BBBBB"], ["CCCCC", "DDDDD"]], "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg", "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg", "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"query": "55555", "response": "66666", "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
|
||||
{"query": "eeeee", "response": "fffff", "history": [], "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
|
||||
{"query": "EEEEE", "response": "FFFFF", "history": [["AAAAA", "BBBBB"], ["CCCCC", "DDDDD"]], "images": ["https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"]}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"system": "00000", "conversation": [{"human": "11111", "assistant": "22222"}]}
|
||||
{"conversation": [{"human": "aaaaa", "assistant": "bbbbb"}]}
|
||||
{"conversation": [{"human": "AAAAA", "assistant": "BBBBB"}, {"human": "CCCCC", "assistant": "DDDDD"}, {"human": "EEEEE", "assistant": "FFFFF"}]}
|
||||
@@ -0,0 +1,3 @@
|
||||
[{"system": "00000", "query": "55555", "response": "66666"},
|
||||
{"query": "eeeee", "response": "fffff", "history": []},
|
||||
{"query": "EEEEE", "response": "FFFFF", "history": [["AAAAA", "BBBBB"], ["CCCCC", "DDDDD"]]}]
|
||||
@@ -0,0 +1,3 @@
|
||||
{"system": "00000", "query": "55555", "response": "66666"}
|
||||
{"query": "eeeee", "response": "fffff", "history": []}
|
||||
{"query": "EEEEE", "response": "FFFFF", "history": [["AAAAA", "BBBBB"], ["CCCCC", "DDDDD"]]}
|
||||
@@ -0,0 +1,4 @@
|
||||
response
|
||||
11111
|
||||
aaaaa
|
||||
AAAAA
|
||||
|
@@ -0,0 +1,3 @@
|
||||
{"response": "11111"}
|
||||
{"response": "aaaaa"}
|
||||
{"response": "AAAAA"}
|
||||
@@ -0,0 +1,4 @@
|
||||
system,query,response
|
||||
00000,11111,22222
|
||||
,aaaaa,bbbbb
|
||||
,AAAAA,BBBBB
|
||||
|
@@ -0,0 +1,3 @@
|
||||
{"system": "00000", "query": "11111", "response": "22222"}
|
||||
{"query": "aaaaa", "response": "bbbbb"}
|
||||
{"query": "AAAAA", "response": "BBBBB"}
|
||||
@@ -0,0 +1,73 @@
|
||||
# Copyright (c) ModelScope Contributors. All rights reserved.
|
||||
import torch
|
||||
import unittest
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from swift.dataset import DatasetMeta, ResponsePreprocessor, load_dataset, register_dataset
|
||||
from swift.infer_engine import InferRequest, RequestConfig, TransformersEngine
|
||||
from swift.model import Model, ModelGroup, ModelMeta, register_model
|
||||
from swift.template import TemplateMeta, register_template
|
||||
|
||||
|
||||
class CustomPreprocessor(ResponsePreprocessor):
|
||||
prompt = """Task: Based on the given two sentences, provide a similarity score between 0.0 and 5.0.
|
||||
Sentence 1: {text1}
|
||||
Sentence 2: {text2}
|
||||
Similarity score: """
|
||||
|
||||
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
return super().preprocess({
|
||||
'query': self.prompt.format(text1=row['text1'], text2=row['text2']),
|
||||
'response': f"{row['label']:.1f}"
|
||||
})
|
||||
|
||||
|
||||
register_dataset(
|
||||
DatasetMeta(
|
||||
ms_dataset_id='swift/stsb',
|
||||
hf_dataset_id='SetFit/stsb',
|
||||
preprocess_func=CustomPreprocessor(),
|
||||
))
|
||||
|
||||
register_template(
|
||||
TemplateMeta(
|
||||
template_type='custom',
|
||||
prefix=['<extra_id_0>System\n{{SYSTEM}}\n'],
|
||||
prompt=['<extra_id_1>User\n{{QUERY}}\n<extra_id_1>Assistant\n'],
|
||||
chat_sep=['\n']))
|
||||
|
||||
register_model(
|
||||
ModelMeta(
|
||||
model_type='custom',
|
||||
model_groups=[
|
||||
ModelGroup([Model('AI-ModelScope/Nemotron-Mini-4B-Instruct', 'nvidia/Nemotron-Mini-4B-Instruct')])
|
||||
],
|
||||
template='custom',
|
||||
ignore_patterns=['nemo']))
|
||||
|
||||
|
||||
class TestCustom(unittest.TestCase):
|
||||
|
||||
def test_custom_model(self):
|
||||
infer_request = InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}])
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
engine = TransformersEngine('AI-ModelScope/Nemotron-Mini-4B-Instruct', torch_dtype=torch.float16)
|
||||
response = engine.infer([infer_request], request_config)
|
||||
swift_response = response[0].choices[0].message.content
|
||||
|
||||
engine.template.template_backend = 'jinja'
|
||||
response = engine.infer([infer_request], request_config)
|
||||
jinja_response = response[0].choices[0].message.content
|
||||
assert swift_response == jinja_response, (f'swift_response: {swift_response}\njinja_response: {jinja_response}')
|
||||
print(f'response: {swift_response}')
|
||||
|
||||
def test_custom_dataset(self):
|
||||
dataset = load_dataset(['swift/stsb'])[0]
|
||||
assert len(dataset) == 5749
|
||||
assert list(dataset[0].keys()) == ['messages', 'dataset']
|
||||
print(f'dataset: {dataset}')
|
||||
print(f'dataset[0]: {dataset[0]}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,19 @@
|
||||
import unittest
|
||||
|
||||
from swift.dataset import load_dataset
|
||||
|
||||
|
||||
class TestDataset(unittest.TestCase):
|
||||
|
||||
def test_load_v_dataset(self):
|
||||
if not __name__ == '__main__':
|
||||
# ignore citest error in github
|
||||
return
|
||||
|
||||
for ds in ['m3it#1000', 'mantis-instruct#1000', 'llava-med-zh-instruct#1000']:
|
||||
ds = load_dataset(ds)
|
||||
assert len(ds[0]) > 800
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,80 @@
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import transformers
|
||||
import unittest
|
||||
from packaging import version
|
||||
|
||||
from swift import ExportArguments, export_main
|
||||
|
||||
if __name__ == '__main__':
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
|
||||
|
||||
|
||||
class TestTemplate(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
print(('Testing %s.%s' % (type(self).__name__, self._testMethodName)))
|
||||
self.tmp_dir = tempfile.TemporaryDirectory().name
|
||||
|
||||
def tearDown(self):
|
||||
if os.path.exists(self.tmp_dir):
|
||||
shutil.rmtree(self.tmp_dir)
|
||||
super().tearDown()
|
||||
|
||||
@unittest.skip('swift2.0')
|
||||
def test_llama3(self):
|
||||
args = ExportArguments(model_type='llama3-8b-instruct', to_ollama=True, ollama_output_dir=self.tmp_dir)
|
||||
export_main(args)
|
||||
|
||||
template = ('TEMPLATE """{{ if .System }}<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\n'
|
||||
'{{ .System }}<|eot_id|>{{ else }}<|begin_of_text|>{{ end }}{{ if .Prompt }}<|start_header_id|>user'
|
||||
'<|end_header_id|>\n\n{{ .Prompt }}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n'
|
||||
'{{ end }}{{ .Response }}<|eot_id|>"""')
|
||||
|
||||
stop = 'PARAMETER stop "<|eot_id|>"'
|
||||
|
||||
with open(os.path.join(self.tmp_dir, 'Modelfile'), 'r') as f:
|
||||
content = f.read()
|
||||
self.assertTrue(template in content)
|
||||
self.assertTrue(stop in content)
|
||||
|
||||
@unittest.skip('swift2.0')
|
||||
def test_chatglm4(self):
|
||||
if version.parse(transformers.__version__) >= version.parse('4.45'):
|
||||
return
|
||||
|
||||
args = ExportArguments(model_type='glm4-9b-chat', to_ollama=True, ollama_output_dir=self.tmp_dir)
|
||||
export_main(args)
|
||||
|
||||
template = ('TEMPLATE """{{ if .System }}[gMASK] <sop><|system|>\n{{ .System }}{{ else }}'
|
||||
'[gMASK] <sop>{{ end }}{{ if .Prompt }}<|user|>\n{{ .Prompt }}<|assistant|>\n'
|
||||
'{{ end }}{{ .Response }}<|user|>"""')
|
||||
|
||||
stop = 'PARAMETER stop "<|user|>"'
|
||||
|
||||
with open(os.path.join(self.tmp_dir, 'Modelfile'), 'r') as f:
|
||||
content = f.read()
|
||||
self.assertTrue(template in content)
|
||||
self.assertTrue(stop in content)
|
||||
|
||||
@unittest.skip('swift2.0')
|
||||
def test_qwen2(self):
|
||||
args = ExportArguments(model_type='qwen2-7b-instruct', to_ollama=True, ollama_output_dir=self.tmp_dir)
|
||||
export_main(args)
|
||||
|
||||
template = ('TEMPLATE """{{ if .System }}<|im_start|>system\n{{ .System }}<|im_end|>\n{{ else }}{{ end }}'
|
||||
'{{ if .Prompt }}<|im_start|>user\n{{ .Prompt }}<|im_end|>\n<|im_start|>assistant\n'
|
||||
'{{ end }}{{ .Response }}<|im_end|>"""')
|
||||
|
||||
stop = 'PARAMETER stop "<|im_end|>"'
|
||||
|
||||
with open(os.path.join(self.tmp_dir, 'Modelfile'), 'r') as f:
|
||||
content = f.read()
|
||||
self.assertTrue(template in content)
|
||||
self.assertTrue(stop in content)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,458 @@
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
|
||||
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import torch
|
||||
import unittest
|
||||
from datasets import Dataset as HfDataset
|
||||
from functools import partial
|
||||
from modelscope import Model, MsDataset, snapshot_download
|
||||
from torch.nn.utils.rnn import pad_sequence
|
||||
from transformers import AutoTokenizer
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from swift import (InferArguments, RLHFArguments, SftArguments, Trainer, TrainingArguments, get_logger, infer_main,
|
||||
rlhf_main, sft_main)
|
||||
|
||||
NO_EVAL_HUMAN = True
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
kwargs = {
|
||||
'per_device_train_batch_size': 2,
|
||||
'per_device_eval_batch_size': 2,
|
||||
'save_steps': 5,
|
||||
'gradient_accumulation_steps': 4,
|
||||
'num_train_epochs': 1,
|
||||
}
|
||||
|
||||
|
||||
class TestRun(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
print(f'Testing {type(self).__name__}.{self._testMethodName}')
|
||||
self._tmp_dir = tempfile.TemporaryDirectory()
|
||||
self.tmp_dir = self._tmp_dir.name
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tmp_dir)
|
||||
|
||||
def test_template(self):
|
||||
if not __name__ == '__main__':
|
||||
# ignore citest error in github
|
||||
return
|
||||
torch.cuda.empty_cache()
|
||||
output = sft_main(
|
||||
SftArguments(
|
||||
model='Qwen/Qwen1.5-0.5B',
|
||||
tuner_type='full',
|
||||
dataset='DAMO_NLP/jd',
|
||||
val_dataset='DAMO_NLP/jd#20',
|
||||
streaming=True,
|
||||
max_steps=12,
|
||||
**kwargs))
|
||||
last_model_checkpoint = output['last_model_checkpoint']
|
||||
torch.cuda.empty_cache()
|
||||
result = infer_main(InferArguments(model=last_model_checkpoint, load_data_args=True, val_dataset_sample=2))
|
||||
assert len(result[0]['response']) < 20
|
||||
|
||||
def test_hf_hub(self):
|
||||
if not __name__ == '__main__':
|
||||
# ignore citest error in github
|
||||
return
|
||||
torch.cuda.empty_cache()
|
||||
train_dataset_fnames = [
|
||||
'alpaca.csv', 'chatml.jsonl', 'swift_pre.jsonl', 'swift_single.csv', 'swift_multi.jsonl',
|
||||
'swift_multi.json#2'
|
||||
]
|
||||
folder = os.path.join(os.path.dirname(__file__), 'data')
|
||||
dataset = [
|
||||
'llm-wizard/alpaca-gpt4-data-zh#20',
|
||||
'shibing624/alpaca-zh#20',
|
||||
] + [os.path.join(folder, fname) for fname in train_dataset_fnames]
|
||||
output = sft_main(
|
||||
SftArguments(
|
||||
model='Qwen/Qwen1.5-0.5B-Chat-GPTQ-Int4', tuner_type='lora', dataset=dataset, use_hf=True, **kwargs))
|
||||
last_model_checkpoint = output['last_model_checkpoint']
|
||||
torch.cuda.empty_cache()
|
||||
infer_main(InferArguments(adapters=last_model_checkpoint, load_data_args=True, val_dataset_sample=2))
|
||||
|
||||
@unittest.skip('avoid ci error')
|
||||
def test_basic(self):
|
||||
output_dir = 'output'
|
||||
quant_bits_list = [0, 4]
|
||||
train_dataset_fnames = [
|
||||
'alpaca.csv', 'chatml.jsonl', 'swift_pre.jsonl', 'swift_single.csv', 'swift_multi.jsonl',
|
||||
'swift_multi.json#2'
|
||||
]
|
||||
folder = os.path.join(os.path.dirname(__file__), 'data')
|
||||
dataset = [
|
||||
'AI-ModelScope/alpaca-gpt4-data-zh#20',
|
||||
'hurner/alpaca-gpt4-data-zh#20',
|
||||
] + [os.path.join(folder, fname) for fname in train_dataset_fnames]
|
||||
if not __name__ == '__main__':
|
||||
output_dir = self.tmp_dir
|
||||
quant_bits_list = [4]
|
||||
dataset = dataset[:2]
|
||||
for quant_bits in quant_bits_list:
|
||||
if quant_bits == 0:
|
||||
predict_with_generate = False
|
||||
quant_method = None
|
||||
else:
|
||||
predict_with_generate = True
|
||||
quant_method = 'bnb'
|
||||
sft_args = SftArguments(
|
||||
model='Qwen/Qwen2-0.5B-Instruct',
|
||||
quant_bits=quant_bits,
|
||||
eval_steps=5,
|
||||
adam_beta2=0.95,
|
||||
quant_method=quant_method,
|
||||
predict_with_generate=predict_with_generate,
|
||||
dataset=dataset,
|
||||
val_dataset='DAMO_NLP/jd#20',
|
||||
output_dir=output_dir,
|
||||
download_mode='force_redownload',
|
||||
include_num_input_tokens_seen=True,
|
||||
gradient_checkpointing=True,
|
||||
**kwargs)
|
||||
torch.cuda.empty_cache()
|
||||
output = sft_main(sft_args)
|
||||
print(output)
|
||||
best_model_checkpoint = output['best_model_checkpoint']
|
||||
print(f'best_model_checkpoint: {best_model_checkpoint}')
|
||||
if __name__ == '__main__':
|
||||
infer_args = InferArguments(
|
||||
adapters=best_model_checkpoint,
|
||||
merge_lora={
|
||||
0: True,
|
||||
4: False
|
||||
}[quant_bits],
|
||||
load_data_args=NO_EVAL_HUMAN,
|
||||
val_dataset_sample=5)
|
||||
torch.cuda.empty_cache()
|
||||
result = infer_main(infer_args)
|
||||
print(result)
|
||||
# if __name__ == '__main__':
|
||||
# app_ui_main(infer_args)
|
||||
|
||||
def test_vl_audio(self):
|
||||
output_dir = 'output'
|
||||
if not __name__ == '__main__':
|
||||
# ignore citest error in github
|
||||
return
|
||||
model_type_list = ['Qwen/Qwen-VL-Chat', 'Qwen/Qwen-Audio-Chat']
|
||||
dataset_list = [
|
||||
'modelscope/coco_2014_caption:validation#100', 'speech_asr/speech_asr_aishell1_trainsets:validation#100'
|
||||
]
|
||||
for model, dataset in zip(model_type_list, dataset_list):
|
||||
sft_args = SftArguments(
|
||||
model=model,
|
||||
eval_steps=5,
|
||||
dataset=[dataset],
|
||||
output_dir=output_dir,
|
||||
gradient_checkpointing=True,
|
||||
lazy_tokenize=True,
|
||||
disable_tqdm=True,
|
||||
**kwargs)
|
||||
torch.cuda.empty_cache()
|
||||
output = sft_main(sft_args)
|
||||
print(output)
|
||||
best_model_checkpoint = output['best_model_checkpoint']
|
||||
print(f'best_model_checkpoint: {best_model_checkpoint}')
|
||||
infer_args = InferArguments(
|
||||
adapters=best_model_checkpoint,
|
||||
load_data_args=True,
|
||||
stream={
|
||||
'Qwen/Qwen-VL-Chat': True,
|
||||
'Qwen/Qwen-Audio-Chat': False
|
||||
}[model],
|
||||
val_dataset_sample=5)
|
||||
torch.cuda.empty_cache()
|
||||
result = infer_main(infer_args)
|
||||
print(result)
|
||||
|
||||
def test_custom_dataset(self):
|
||||
if not __name__ == '__main__':
|
||||
# ignore citest error in github
|
||||
return
|
||||
train_dataset_fnames = [
|
||||
'alpaca.csv', 'chatml.jsonl', 'swift_pre.jsonl', 'swift_single.csv', 'swift_multi.jsonl',
|
||||
'swift_multi.json', 'sharegpt.jsonl'
|
||||
]
|
||||
val_dataset_fnames = [
|
||||
'alpaca.jsonl',
|
||||
'alpaca2.csv',
|
||||
'conversations.jsonl',
|
||||
'swift_pre.csv',
|
||||
'swift_single.jsonl',
|
||||
# 'swift_#:#.jsonl#3'
|
||||
]
|
||||
folder = os.path.join(os.path.dirname(__file__), 'data')
|
||||
resume_from_checkpoint = None
|
||||
train_kwargs = kwargs.copy()
|
||||
train_kwargs.pop('num_train_epochs')
|
||||
for num_train_epochs in [1, 2]:
|
||||
sft_args = SftArguments(
|
||||
model='Qwen/Qwen-7B-Chat',
|
||||
dataset=['swift/self-cognition#20'] + [os.path.join(folder, fname) for fname in train_dataset_fnames],
|
||||
val_dataset=[os.path.join(folder, fname) for fname in val_dataset_fnames],
|
||||
resume_from_checkpoint=resume_from_checkpoint,
|
||||
num_train_epochs=num_train_epochs,
|
||||
model_name='小黄',
|
||||
model_author='魔搭',
|
||||
**train_kwargs)
|
||||
|
||||
torch.cuda.empty_cache()
|
||||
result = sft_main(sft_args)
|
||||
best_model_checkpoint = result['best_model_checkpoint']
|
||||
resume_from_checkpoint = result['last_model_checkpoint']
|
||||
|
||||
for load_args in [True, False]:
|
||||
infer_kwargs = {}
|
||||
if load_args is False:
|
||||
args_json = os.path.join(best_model_checkpoint, 'args.json')
|
||||
assert os.path.exists(args_json)
|
||||
os.remove(args_json)
|
||||
infer_kwargs = {'model': 'Qwen/Qwen-7B-Chat'}
|
||||
infer_args = InferArguments(
|
||||
adapters=best_model_checkpoint,
|
||||
load_data_args=load_args and NO_EVAL_HUMAN,
|
||||
merge_lora=load_args,
|
||||
val_dataset=[os.path.join(folder, fname) for fname in val_dataset_fnames],
|
||||
**infer_kwargs)
|
||||
torch.cuda.empty_cache()
|
||||
infer_main(infer_args)
|
||||
|
||||
def test_rlhf(self):
|
||||
if not __name__ == '__main__':
|
||||
# ignore citest error in github
|
||||
return
|
||||
torch.cuda.empty_cache()
|
||||
# llm rlhf
|
||||
#
|
||||
rlhf_types = ['dpo', 'orpo', 'simpo', 'kto', 'cpo', 'rm', 'ppo']
|
||||
for rlhf_type in rlhf_types:
|
||||
dataset = ('AI-ModelScope/hh_rlhf_cn:harmless_base_cn#100'
|
||||
if rlhf_type != 'kto' else 'AI-ModelScope/ultrafeedback-binarized-preferences-cleaned-kto#100')
|
||||
train_kwargs = {}
|
||||
if rlhf_type == 'ppo':
|
||||
train_kwargs['reward_model'] = 'Qwen/Qwen2-1.5B-Instruct'
|
||||
output = rlhf_main(
|
||||
RLHFArguments(
|
||||
rlhf_type=rlhf_type,
|
||||
model='Qwen/Qwen2-1.5B-Instruct',
|
||||
dataset=dataset,
|
||||
eval_steps=5,
|
||||
split_dataset_ratio=0.05,
|
||||
**train_kwargs,
|
||||
**kwargs))
|
||||
if rlhf_type == 'ppo':
|
||||
model_checkpoint = output['last_model_checkpoint']
|
||||
else:
|
||||
model_checkpoint = output['best_model_checkpoint']
|
||||
|
||||
torch.cuda.empty_cache()
|
||||
infer_main(InferArguments(adapters=model_checkpoint, load_data_args=True))
|
||||
|
||||
# mllm rlhf
|
||||
visual_rlhf_types = ['dpo', 'orpo', 'simpo', 'cpo', 'rm']
|
||||
test_model = [
|
||||
'OpenGVLab/InternVL2-2B', 'Qwen/Qwen2-VL-2B-Instruct', 'llava-hf/llava-v1.6-mistral-7b-hf',
|
||||
'AI-ModelScope/Florence-2-base-ft'
|
||||
] # decoder only and encoder-decoder
|
||||
for rlhf_type in visual_rlhf_types:
|
||||
for model in test_model:
|
||||
dataset_name = 'swift/RLAIF-V-Dataset#100'
|
||||
output = rlhf_main(
|
||||
RLHFArguments(
|
||||
rlhf_type=rlhf_type,
|
||||
model=model,
|
||||
dataset=dataset_name,
|
||||
eval_steps=5,
|
||||
dataset_num_proc=16,
|
||||
**kwargs))
|
||||
best_model_checkpoint = output['best_model_checkpoint']
|
||||
torch.cuda.empty_cache()
|
||||
infer_main(InferArguments(adapters=best_model_checkpoint, load_data_args=True, val_dataset_sample=2))
|
||||
|
||||
def test_loss_matching(self):
|
||||
output_dir = 'output'
|
||||
if not __name__ == '__main__':
|
||||
# ignore citest error in github
|
||||
return
|
||||
losses = []
|
||||
for use_swift_lora in [False, True]:
|
||||
bool_var = use_swift_lora
|
||||
torch.cuda.empty_cache()
|
||||
output = sft_main([
|
||||
'--model', 'Qwen/Qwen-7B-Chat', '--save_steps', '5', '--dataset',
|
||||
'AI-ModelScope/leetcode-solutions-python#200', '--output_dir', output_dir, '--gradient_checkpointing',
|
||||
'true', '--max_new_tokens', '100', '--attn_impl', 'flash_attn', '--target_modules', 'all-linear',
|
||||
'--seed', '0', '--lora_bias', 'all', '--modules_to_save', 'lm_head', '--use_swift_lora',
|
||||
str(use_swift_lora), '--num_train_epochs', '1', '--gradient_accumulation_steps', '16'
|
||||
])
|
||||
best_model_checkpoint = output['best_model_checkpoint']
|
||||
print(f'best_model_checkpoint: {best_model_checkpoint}')
|
||||
load_data_args = str(bool_var or NO_EVAL_HUMAN)
|
||||
if load_data_args:
|
||||
val_dataset_sample = 2
|
||||
else:
|
||||
val_dataset_sample = -1
|
||||
torch.cuda.empty_cache()
|
||||
infer_main([
|
||||
'--adapters', best_model_checkpoint, '--val_dataset_sample',
|
||||
str(val_dataset_sample), '--max_new_tokens', '100', '--attn_impl', 'eager', '--merge_lora',
|
||||
str(bool_var), '--load_data_args',
|
||||
str(load_data_args)
|
||||
])
|
||||
loss = output['log_history'][-1]['train_loss']
|
||||
losses.append(loss)
|
||||
self.assertTrue(abs(losses[0] - losses[1]) < 5e-4)
|
||||
print(f'swift_loss: {losses[0]}')
|
||||
print(f'peft_loss: {losses[1]}')
|
||||
self.assertTrue(0.95 <= losses[0] <= 1)
|
||||
|
||||
def test_pai_compat(self):
|
||||
if not __name__ == '__main__':
|
||||
# ignore citest error in github
|
||||
return
|
||||
from swift import infer_main, sft_main
|
||||
os.environ['PAI_TRAINING_JOB_ID'] = '123456'
|
||||
folder = os.path.join(os.path.dirname(__file__), 'config')
|
||||
tensorboard_dir = os.path.join('output/pai_test', 'pai_tensorboard')
|
||||
os.environ['PAI_OUTPUT_TENSORBOARD'] = tensorboard_dir
|
||||
sft_json = os.path.join(folder, 'sft.json')
|
||||
infer_json = os.path.join(folder, 'infer.json')
|
||||
torch.cuda.empty_cache()
|
||||
output = sft_main([sft_json])
|
||||
print()
|
||||
infer_args = {
|
||||
'adapters': output['best_model_checkpoint'],
|
||||
'val_dataset_sample': 2,
|
||||
'load_data_args': True,
|
||||
}
|
||||
import json
|
||||
with open(infer_json, 'w') as f:
|
||||
json.dump(infer_args, f, ensure_ascii=False, indent=4)
|
||||
torch.cuda.empty_cache()
|
||||
infer_main([infer_json])
|
||||
os.environ.pop('PAI_TRAINING_JOB_ID')
|
||||
|
||||
|
||||
def data_collate_fn(batch: List[Dict[str, Any]], tokenizer) -> Dict[str, torch.Tensor]:
|
||||
# text-classification
|
||||
assert tokenizer.pad_token_id is not None
|
||||
input_ids = [torch.tensor(b['input_ids']) for b in batch]
|
||||
labels = torch.tensor([b['labels'] for b in batch])
|
||||
attention_mask = [torch.ones(len(input_ids[i]), dtype=torch.int64) for i in range(len(input_ids))]
|
||||
|
||||
input_ids = pad_sequence(input_ids, batch_first=True, padding_value=tokenizer.pad_token_id)
|
||||
attention_mask = pad_sequence(attention_mask, batch_first=True, padding_value=0)
|
||||
return {'input_ids': input_ids, 'attention_mask': attention_mask, 'labels': labels}
|
||||
|
||||
|
||||
class BertTrainer(Trainer):
|
||||
|
||||
def compute_loss(self, model, inputs, return_outputs=False):
|
||||
outputs = model(**inputs)
|
||||
loss = outputs.loss
|
||||
if loss is None:
|
||||
logits, loss = list(outputs.logits)
|
||||
return (loss, outputs) if return_outputs else loss
|
||||
|
||||
|
||||
class TestTrainer(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._tmp_dir = tempfile.TemporaryDirectory()
|
||||
self.tmp_dir = self._tmp_dir.name
|
||||
# self.tmp_dir = 'test'
|
||||
logger.info(f'self.tmp_dir: {self.tmp_dir}')
|
||||
|
||||
def tearDown(self):
|
||||
if os.path.isdir(self.tmp_dir):
|
||||
shutil.rmtree(self.tmp_dir)
|
||||
# api = HubApi()
|
||||
# api.delete_model(self.hub_model_id)
|
||||
# logger.info(f'delete model: {self.hub_model_id}')
|
||||
|
||||
def test_trainer(self):
|
||||
self.hub_model_id = 'test_trainer2'
|
||||
logger.info(f'self.hub_model_id: {self.hub_model_id}')
|
||||
self.tmp_dir = 'output/damo/nlp_structbert_backbone_base_std'
|
||||
push_to_hub = True
|
||||
if not __name__ == '__main__':
|
||||
# ignore citest error in github
|
||||
return
|
||||
model_id = 'damo/nlp_structbert_backbone_base_std'
|
||||
model_dir = snapshot_download(model_id, 'master')
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_dir)
|
||||
dataset = MsDataset.load('clue', subset_name='tnews')
|
||||
num_labels = max(dataset['train']['label']) + 1
|
||||
model = Model.from_pretrained(model_dir, task='text-classification', num_labels=num_labels)
|
||||
train_dataset, val_dataset = dataset['train'].to_hf_dataset(), dataset['validation'].to_hf_dataset()
|
||||
train_dataset: HfDataset = train_dataset.select(range(100))
|
||||
val_dataset: HfDataset = val_dataset.select(range(20))
|
||||
|
||||
#
|
||||
def tokenize_func(examples):
|
||||
data = tokenizer(examples['sentence'], return_attention_mask=False)
|
||||
examples['input_ids'] = data['input_ids']
|
||||
examples['labels'] = examples['label']
|
||||
del examples['sentence'], examples['label']
|
||||
return examples
|
||||
|
||||
train_dataset = train_dataset.map(tokenize_func)
|
||||
val_dataset = val_dataset.map(tokenize_func)
|
||||
|
||||
data_collator = partial(data_collate_fn, tokenizer=tokenizer)
|
||||
for save_only_model in [True, False]:
|
||||
trainer_args = TrainingArguments(
|
||||
self.tmp_dir,
|
||||
do_train=True,
|
||||
do_eval=True,
|
||||
num_train_epochs=1,
|
||||
evaluation_strategy='steps',
|
||||
save_strategy='steps',
|
||||
per_device_train_batch_size=4,
|
||||
per_device_eval_batch_size=4,
|
||||
push_to_hub=push_to_hub,
|
||||
hub_token=None, # use env var
|
||||
hub_private_repo=True,
|
||||
hub_strategy='every_save',
|
||||
hub_model_id=self.hub_model_id,
|
||||
overwrite_output_dir=True,
|
||||
save_steps=10,
|
||||
save_total_limit=2,
|
||||
metric_for_best_model='loss',
|
||||
greater_is_better=False,
|
||||
report_to=['tensorboard'],
|
||||
gradient_accumulation_steps=1,
|
||||
logging_steps=5,
|
||||
eval_steps=10,
|
||||
save_safetensors=False,
|
||||
save_only_model=save_only_model)
|
||||
trainer_args._n_gpu = 1
|
||||
trainer = BertTrainer(model, trainer_args, data_collator, train_dataset, val_dataset, tokenizer)
|
||||
self.hub_model_id = trainer_args.hub_model_id
|
||||
trainer.train()
|
||||
if trainer_args.push_to_hub:
|
||||
trainer.push_to_hub()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# TestRun().test_template()
|
||||
# TestRun().test_hf_hub()
|
||||
# TestRun().test_basic()
|
||||
# TestRun().test_custom_dataset()
|
||||
# TestRun().test_vl_audio()
|
||||
# TestRun().test_loss_matching()
|
||||
#
|
||||
# TestRun().test_rlhf()
|
||||
unittest.main()
|
||||
@@ -0,0 +1,109 @@
|
||||
import os
|
||||
import torch
|
||||
import unittest
|
||||
|
||||
from swift.infer_engine import RequestConfig, TransformersEngine
|
||||
from swift.model import get_processor
|
||||
from swift.template import get_template
|
||||
from swift.utils import get_logger, seed_everything
|
||||
|
||||
# os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
# os.environ['ASCEND_RT_VISIBLE_DEVICES'] = '0'
|
||||
os.environ['SWIFT_DEBUG'] = '1'
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
def _infer_model(engine, system=None, messages=None):
|
||||
seed_everything(42)
|
||||
request_config = RequestConfig(max_tokens=128, temperature=0)
|
||||
if messages is None:
|
||||
messages = []
|
||||
if system is not None:
|
||||
messages += [{'role': 'system', 'content': system}]
|
||||
messages += [{'role': 'user', 'content': '你好'}]
|
||||
resp = engine.infer([{'messages': messages}], request_config=request_config)
|
||||
response = resp[0].choices[0].message.content
|
||||
messages += [{'role': 'assistant', 'content': response}, {'role': 'user', 'content': '<image>这是什么'}]
|
||||
resp = engine.infer([{
|
||||
'messages': messages,
|
||||
}], request_config=request_config)
|
||||
response = resp[0].choices[0].message.content
|
||||
messages += [{'role': 'assistant', 'content': response}]
|
||||
logger.info(f'model: {engine.model_info.model_name}, messages: {messages}')
|
||||
return response
|
||||
|
||||
|
||||
class TestTemplate(unittest.TestCase):
|
||||
|
||||
@unittest.skipIf(not torch.cuda.is_available(), reason='GPTQ is only available on GPU')
|
||||
def test_template(self):
|
||||
engine = TransformersEngine('Qwen/Qwen2.5-3B-Instruct-GPTQ-Int4')
|
||||
response = _infer_model(engine)
|
||||
engine.template.template_backend = 'jinja'
|
||||
response2 = _infer_model(engine)
|
||||
assert response == response2
|
||||
|
||||
def test_tool_message_join(self):
|
||||
from copy import deepcopy
|
||||
|
||||
from swift.agent_template import agent_template_map
|
||||
|
||||
messages = [
|
||||
# first round
|
||||
{
|
||||
'role': 'user',
|
||||
'content': 'user1'
|
||||
},
|
||||
{
|
||||
'role': 'assistant',
|
||||
'content': 'assistant1'
|
||||
},
|
||||
{
|
||||
'role': 'assistant',
|
||||
'content': 'assistant2'
|
||||
},
|
||||
{
|
||||
'role': 'tool',
|
||||
'content': 'tool1'
|
||||
},
|
||||
# second round
|
||||
{
|
||||
'role': 'assistant',
|
||||
'content': 'assistant3'
|
||||
},
|
||||
{
|
||||
'role': 'tool',
|
||||
'content': 'tool2'
|
||||
},
|
||||
{
|
||||
'role': 'tool',
|
||||
'content': 'tool3'
|
||||
},
|
||||
]
|
||||
|
||||
# testing two template type.
|
||||
tokenizer = get_processor('Qwen/Qwen2.5-7B-Instruct')
|
||||
template = get_template(tokenizer)
|
||||
for agent_template_type in ('react_zh', 'qwen_zh'):
|
||||
template._agent_template = agent_template_type
|
||||
agent_template = template.agent_template
|
||||
observation = agent_template.keyword.observation
|
||||
test_messages = deepcopy(messages)
|
||||
test_messages[2]['content'] = 'assistant2' + observation
|
||||
test_messages[4]['content'] = (
|
||||
agent_template.keyword.action + agent_template.keyword.action_input + 'assistant3' + observation)
|
||||
encoded = template.encode({'messages': test_messages})
|
||||
res = template.safe_decode(encoded['input_ids'])
|
||||
|
||||
ground_truth = (
|
||||
'<|im_start|>system\nYou are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>\n'
|
||||
'<|im_start|>user\nuser1<|im_end|>\n'
|
||||
f'<|im_start|>assistant\nassistant1assistant2{observation}tool1'
|
||||
f'{agent_template.keyword.action}{agent_template.keyword.action_input}assistant3'
|
||||
f'{observation}tool2\n{observation}tool3\n')
|
||||
assert res == ground_truth
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,28 @@
|
||||
import unittest
|
||||
|
||||
from swift.dataset import load_dataset
|
||||
from swift.utils import lower_bound
|
||||
|
||||
|
||||
class TestLlmUtils(unittest.TestCase):
|
||||
|
||||
def test_count_startswith(self):
|
||||
arr = [-100] * 1000 + list(range(1000))
|
||||
self.assertTrue(lower_bound(0, len(arr), lambda i: arr[i] != -100) == 1000)
|
||||
|
||||
def test_count_endswith(self):
|
||||
arr = list(range(1000)) + [-100] * 1000
|
||||
self.assertTrue(lower_bound(0, len(arr), lambda i: arr[i] == -100) == 1000)
|
||||
|
||||
@unittest.skip('avoid ci error')
|
||||
def test_dataset(self):
|
||||
dataset = load_dataset(['AI-ModelScope/alpaca-gpt4-data-zh#1000', 'AI-ModelScope/alpaca-gpt4-data-en#200'],
|
||||
num_proc=4,
|
||||
strict=False,
|
||||
download_mode='force_redownload')
|
||||
print(f'dataset[0]: {dataset[0]}')
|
||||
print(f'dataset[1]: {dataset[1]}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,5 @@
|
||||
from swift.arguments import WebUIArguments
|
||||
from swift.ui import webui_main
|
||||
|
||||
if __name__ == '__main__':
|
||||
webui_main(WebUIArguments())
|
||||
Reference in New Issue
Block a user