Translation
Transformers
Safetensors
llama
text-generation
multilingual
lora
astheraltranslate
text-generation-inference
Instructions to use SEE7/AstheralTranslate with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use SEE7/AstheralTranslate with Transformers:
# Use a pipeline as a high-level helper # Warning: Pipeline type "translation" is no longer supported in transformers v5. # You must load the model directly (see below) or downgrade to v4.x with: # pip install "transformers<5.0.0" from transformers import pipeline pipe = pipeline("translation", model="SEE7/AstheralTranslate")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("SEE7/AstheralTranslate") model = AutoModelForCausalLM.from_pretrained("SEE7/AstheralTranslate", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download train.py from SEE7/AstheralTranslate: direct link, hf CLI and curl.
- Browser
- Download file 6.66 kB
-
https://huggingface.co/SEE7/AstheralTranslate/resolve/main/train.py
- Command line
-
hf download hf://SEE7/AstheralTranslate/train.py
-
curl -L -o train.py https://huggingface.co/SEE7/AstheralTranslate/resolve/main/train.py
6.66 kB
| import os,json,time,random,gc,hashlib | |
| from pathlib import Path | |
| import torch | |
| from datasets import load_dataset | |
| from transformers import AutoTokenizer,AutoModelForCausalLM,Trainer,TrainingArguments,set_seed | |
| from peft import LoraConfig,get_peft_model | |
| from huggingface_hub import HfApi,hf_hub_download | |
| from sacrebleu.metrics import CHRF,BLEU | |
| BASE='utter-project/EuroLLM-1.7B';REPO='SEE7/AstheralTranslate';DATA='Helsinki-NLP/opus-100' | |
| api=HfApi();rev=api.model_info(BASE).sha;datarev=api.dataset_info(DATA).sha | |
| out=Path('/tmp/translate');out.mkdir(exist_ok=True) | |
| set_seed(42);rng=random.Random(42);start=time.monotonic() | |
| langs={'de':'German','fr':'French','es':'Spanish','it':'Italian','pt':'Portuguese'} | |
| tok=AutoTokenizer.from_pretrained(BASE,revision=rev);tok.pad_token=tok.eos_token;tok.padding_side='left' | |
| def prompt(text,lang):return 'Translate from English to '+langs[lang]+'. Return only the translation.\nEnglish: '+text.strip()+'\n'+langs[lang]+':' | |
| train=[];tests=[];seen=set() | |
| for lang in langs: | |
| conf='-'.join(sorted(['en',lang])) | |
| ds=load_dataset(DATA,conf,split='train',revision=datarev) | |
| ds=ds.shuffle(seed=42) | |
| count=0 | |
| for row in ds: | |
| x=row['translation'];a=x['en'].strip();b=x[lang].strip() | |
| if not (8<=len(a)<=600 and 4<=len(b)<=700):continue | |
| key=(lang,a,b) | |
| if key in seen:continue | |
| pre=tok(prompt(a,lang),add_special_tokens=True)['input_ids'] | |
| answer=tok(' '+b+tok.eos_token,add_special_tokens=False)['input_ids'] | |
| if len(pre)+len(answer)>256:continue | |
| seen.add(key);train.append({'input_ids':pre+answer,'labels':[-100]*len(pre)+answer,'source':a,'target':b,'language':lang}) | |
| count+=1 | |
| if count==2000:break | |
| print('DATA',lang,count,flush=True) | |
| test=load_dataset(DATA,conf,split='test',revision=datarev) | |
| for row in test: | |
| x=row['translation'];a=x['en'].strip();b=x[lang].strip() | |
| if 8<=len(a)<=300 and 4<=len(b)<=400 and (lang,a,b) not in seen: | |
| tests.append({'source':a,'reference':b,'language':lang}) | |
| if sum(t['language']==lang for t in tests)==25:break | |
| assert len(tests)==125 | |
| rng.shuffle(train) | |
| (out/'training_manifest.json').write_text(json.dumps({'base':BASE,'revision':rev,'dataset':DATA,'dataset_revision':datarev,'examples':len(train),'per_language':2000,'seed':42,'sha256':hashlib.sha256(json.dumps(train,sort_keys=True).encode()).hexdigest()},indent=2)) | |
| model=AutoModelForCausalLM.from_pretrained(BASE,revision=rev,torch_dtype=torch.bfloat16,device_map={'':'cuda'},attn_implementation='sdpa').eval() | |
| report={'base':BASE,'base_revision':rev,'dataset_revision':datarev,'parameters':sum(p.numel() for p in model.parameters()),'languages':langs,'hardware':torch.cuda.get_device_name(),'training_examples':len(train)} | |
| def evaluate(label): | |
| rows=[] | |
| for i,t in enumerate(tests): | |
| x=tok(prompt(t['source'],t['language']),return_tensors='pt').to('cuda') | |
| y=model.generate(**x,max_new_tokens=128,do_sample=False,pad_token_id=tok.pad_token_id,eos_token_id=tok.eos_token_id) | |
| raw=tok.decode(y[0,x['input_ids'].shape[1]:],skip_special_tokens=True) | |
| pred=raw.strip().split('\n')[0].strip() | |
| rows.append({**t,'prediction':pred,'raw':raw,'generated_tokens':int(y.shape[1]-x['input_ids'].shape[1])}) | |
| if i%10==0:print('EVAL',label,i,flush=True) | |
| scores={} | |
| for lang in langs: | |
| r=[r for r in rows if r['language']==lang];hyp=[r['prediction'] for r in r];refs=[[r['reference'] for r in r]] | |
| scores[lang]={'chrf':CHRF().corpus_score(hyp,refs).score,'bleu':BLEU().corpus_score(hyp,refs).score,'n':len(r)} | |
| (out/(label+'_outputs.json')).write_text(json.dumps(rows,ensure_ascii=False,indent=2)) | |
| print('SCORES',label,json.dumps(scores),flush=True) | |
| return scores | |
| report['base_scores']=evaluate('base') | |
| model=get_peft_model(model,LoraConfig(r=32,lora_alpha=64,lora_dropout=0.05,target_modules=['q_proj','k_proj','v_proj','o_proj','gate_proj','up_proj','down_proj'],task_type='CAUSAL_LM')) | |
| report['trainable_parameters']=sum(p.numel() for p in model.parameters() if p.requires_grad) | |
| model.config.use_cache=False | |
| def collate(rows): | |
| length=max(len(r['input_ids']) for r in rows) | |
| return {'input_ids':torch.tensor([[tok.pad_token_id]*(length-len(r['input_ids']))+r['input_ids'] for r in rows]),'labels':torch.tensor([[-100]*(length-len(r['labels']))+r['labels'] for r in rows]),'attention_mask':torch.tensor([[0]*(length-len(r['input_ids']))+[1]*len(r['input_ids']) for r in rows])} | |
| args=TrainingArguments(output_dir='/tmp/checkpoints',max_steps=500,per_device_train_batch_size=4,gradient_accumulation_steps=4,learning_rate=1e-4,warmup_ratio=0.05,bf16=True,gradient_checkpointing=True,gradient_checkpointing_kwargs={'use_reentrant':False},save_strategy='no',logging_steps=20,report_to='none',remove_unused_columns=False,seed=42) | |
| trainer=Trainer(model=model,args=args,train_dataset=train,data_collator=collate) | |
| result=trainer.train();report['training']=result.metrics;report['optimizer_steps']=trainer.state.global_step | |
| model.save_pretrained(out/'adapter');model.gradient_checkpointing_disable();model.config.use_cache=True;model.eval() | |
| model=model.merge_and_unload(safe_merge=True);model.config.astheral_version='translate-1' | |
| model.save_pretrained(out/'model',safe_serialization=True,max_shard_size='4GB');tok.save_pretrained(out/'model') | |
| del trainer,model;gc.collect();torch.cuda.empty_cache() | |
| model=AutoModelForCausalLM.from_pretrained(out/'model',torch_dtype=torch.bfloat16,device_map={'':'cuda'},attn_implementation='sdpa').eval() | |
| report['finetuned_scores']=evaluate('finetuned') | |
| report['mean_chrf_base']=sum(v['chrf'] for v in report['base_scores'].values())/5 | |
| report['mean_chrf_finetuned']=sum(v['chrf'] for v in report['finetuned_scores'].values())/5 | |
| report['improved']=report['mean_chrf_finetuned']>report['mean_chrf_base'] | |
| report['elapsed_seconds']=time.monotonic()-start | |
| (out/'evaluation.json').write_text(json.dumps(report,indent=2)) | |
| (out/'train.py').write_text(Path(__file__).read_text()) | |
| api.upload_folder(repo_id=REPO,folder_path=str(out/'model'),commit_message='Publish actual merged AstheralTranslate weights after reload and translation evaluation') | |
| api.upload_folder(repo_id=REPO,folder_path=str(out/'adapter'),path_in_repo='adapter',commit_message='Publish trained rank32 translation adapter') | |
| api.upload_folder(repo_id=REPO,folder_path=str(out),allow_patterns=['*.json','*.py'],commit_message='Publish translation training provenance and before/after test outputs') | |
| import urllib.request | |
| license_path=str(out/'LICENSE') | |
| Path(license_path).write_bytes(urllib.request.urlopen('https://www.apache.org/licenses/LICENSE-2.0.txt',timeout=20).read()) | |
| api.upload_file(repo_id=REPO,path_or_fileobj=license_path,path_in_repo='LICENSE') | |
| print('TRANSLATE_COMPLETE',json.dumps(report),flush=True) | |