Image-Text-to-Text
Transformers
Safetensors
Chinese
English
lfm2_titans
custom_code
lfm2
multimodal
persistent-memory
titans
differentiable-neural-computer
male-cns
conversational
Instructions to use win10/LFM-MD-V1-VL-3B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use win10/LFM-MD-V1-VL-3B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="win10/LFM-MD-V1-VL-3B", trust_remote_code=True) messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForImageTextToText model = AutoModelForImageTextToText.from_pretrained("win10/LFM-MD-V1-VL-3B", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use win10/LFM-MD-V1-VL-3B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "win10/LFM-MD-V1-VL-3B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "win10/LFM-MD-V1-VL-3B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/win10/LFM-MD-V1-VL-3B
- SGLang
How to use win10/LFM-MD-V1-VL-3B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "win10/LFM-MD-V1-VL-3B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "win10/LFM-MD-V1-VL-3B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "win10/LFM-MD-V1-VL-3B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "win10/LFM-MD-V1-VL-3B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use win10/LFM-MD-V1-VL-3B with Docker Model Runner:
docker model run hf.co/win10/LFM-MD-V1-VL-3B
Download native_sft_memory.py from win10/LFM-MD-V1-VL-3B: direct link, hf CLI and curl.
- Browser
- Download file 14.8 kB
-
https://huggingface.co/win10/LFM-MD-V1-VL-3B/resolve/main/native_sft_memory.py
- Command line
-
hf download hf://win10/LFM-MD-V1-VL-3B/native_sft_memory.py
-
curl -L -o native_sft_memory.py https://huggingface.co/win10/LFM-MD-V1-VL-3B/resolve/main/native_sft_memory.py
14.8 kB
| """Whole-example joint memory loss; native LlamaFactory owns training. | |
| There is no SFT window/session scheduler. FFN updates reuse the native | |
| supervised backward; the model returns native logits and CE plus memory loss. | |
| """ | |
| from contextvars import ContextVar | |
| from dataclasses import replace | |
| from pathlib import Path | |
| import json,time,os | |
| import torch | |
| from torch.nn import functional as F | |
| from transformers import TrainerCallback | |
| from .batched_memory import BatchedMemoryBlock | |
| from .episodic_adapters import AdapterBatch | |
| from .sft_lora import save_custom_lora,shared_effective_weights,warm_effective_weights | |
| def sampled_pairs(features,count,temporal): | |
| x=features.detach().float().reshape(-1,features.shape[-1]) | |
| available=len(x)-1 if temporal else len(x) | |
| if available<1:return None | |
| indices=torch.linspace(0,available-1,min(count,available),device=x.device).long() | |
| return x[indices],x[indices+1] if temporal else x[indices] | |
| class NativeSFTMemory: | |
| def __init__(self,model): | |
| self.model=model;self.batches=0;self.last={};self.gradient_scale=1.;self.eval_reports=[] | |
| self.inputs=ContextVar(f'joint_memory_inputs_{id(model)}',default=None) | |
| def capture(module,args,kwargs): | |
| target=self.inputs.get() | |
| if target is not None:target.append(kwargs['inputs_embeds'].detach()) | |
| model.model.language_model.register_forward_pre_hook(capture,with_kwargs=True) | |
| def forward(self,native_forward,inputs): | |
| with shared_effective_weights(): | |
| if getattr(self.model.config,'native_memory_recall',None): | |
| from .memory_recall_training import forward,evaluation_parameters | |
| if not self.model.training and not self.eval_reports and torch.cuda.is_available(): | |
| torch.cuda.reset_peak_memory_stats() | |
| # Evaluation needs only the source-only inner write gradient. | |
| # Query scoring, readers and meta-gradients must remain disabled. | |
| with evaluation_parameters(self.model),torch.set_grad_enabled(self.model.training): | |
| output=forward(self,native_forward,inputs) | |
| if not self.model.training: | |
| if torch.cuda.is_available():self.last['peak_allocated_gib']=torch.cuda.max_memory_allocated()/2**30 | |
| self.eval_reports.append(dict(self.last)) | |
| return output | |
| return self._forward(native_forward,inputs) | |
| def _forward(self,native_forward,inputs): | |
| model=self.model;start=time.monotonic();ids=inputs['input_ids'];attention=inputs.get('attention_mask') | |
| events=[torch.cuda.Event(enable_timing=True) for _ in range(5)] if ids.is_cuda else None | |
| if events:events[0].record() | |
| warm_effective_weights(model) | |
| if events:events[1].record() | |
| if attention is None:attention=torch.ones_like(ids) | |
| units=[model.initial_physical_memory(create_graph=True) for _ in ids] | |
| first_answer=(inputs['labels']!=-100).long().argmax(-1) | |
| observed=attention.bool() & (torch.arange(ids.shape[1],device=ids.device)[None]<first_answer[:,None]) | |
| blocks=BatchedMemoryBlock(model,units,ids,inputs.get('spatial_shapes'),observed,fresh_rows=[True]*len(units)) | |
| token=model._memory_context.set((blocks,True,attention));captured=[];scope=self.inputs.set(captured) | |
| attention_features={};attention_scope=model._attention_capture_context.set(attention_features) | |
| labels=inputs['labels'];denominator=inputs.get('num_items_in_batch') | |
| if denominator is None:denominator=(labels[:,1:]!=-100).sum() | |
| denominator=denominator.clamp_min(1) if torch.is_tensor(denominator) else max(1,denominator) | |
| try: | |
| with model._episodic_adapter_bank.use(model._episodic_adapter_bank.query_state(AdapterBatch(tuple(u.adapters for u in units)))): | |
| output=native_forward(**dict(inputs,use_cache=False,return_dict=True,num_items_in_batch=denominator)) | |
| finally:model._memory_context.reset(token);self.inputs.reset(scope);model._attention_capture_context.reset(attention_scope) | |
| if events:events[2].record() | |
| forward_seconds=time.monotonic()-start;tick=time.monotonic() | |
| # FFN update gradients are collected by the native Trainer backward. | |
| if events:events[3].record() | |
| gradient_seconds=time.monotonic()-tick;tick=time.monotonic() | |
| count=getattr(model.config,'native_sft_aux_features',32) | |
| if type(count) is not int or count<2:raise ValueError('at least two auxiliary observations required') | |
| losses=[];read_before=[];read_after=[];feature_count=0 | |
| for row,(unit,block) in enumerate(zip(units,blocks.blocks)): | |
| writer=model.memory.begin(unit.graph);pairs={} | |
| for name,features in block.native_observations.items(): | |
| pair=sampled_pairs(features,count,name!=model.config.vision_port) | |
| if pair is None:continue | |
| key,value=pair;pairs[name]=pair | |
| writer.observe(name,name,torch.stack((key,value),dim=1) if name!=model.config.vision_port else key) | |
| feature_count+=len(key) | |
| written_graph,_=writer.commit(create_graph=True) | |
| written=replace(unit,graph=written_graph);recalls=[] | |
| for name,(key,value) in pairs.items(): | |
| target=F.layer_norm(value,(value.shape[-1],));gate=model.memory.ports[name].residual_gate.sigmoid() | |
| prediction=model.memory.read(name,key,written.graph.fast)*gate | |
| recalls.append(F.mse_loss(prediction,target)) | |
| with torch.no_grad(): | |
| prior=model.memory.read(name,key,unit.graph.fast.detach())*gate.detach() | |
| read_before.append(float(F.mse_loss(prior,target)));read_after.append(float(recalls[-1].detach())) | |
| if recalls:losses.append(torch.stack(recalls).mean()) | |
| from .attention_memory import functional_reader_loss | |
| dnc_loss=functional_reader_loss(model,attention_features,labels,attention.bool()) | |
| graph_loss=torch.stack(losses).mean() if losses else dnc_loss.new_zeros(()) | |
| auxiliary=getattr(model.config,'native_sft_memory_loss_weight',.05)*(graph_loss+dnc_loss) | |
| supervised=output.loss;output.loss=supervised+auxiliary | |
| if not torch.isfinite(output.loss):raise FloatingPointError('nonfinite joint SFT loss') | |
| if events:events[4].record();events[4].synchronize() | |
| self.batches+=1 | |
| self.last=dict(batch=self.batches,physical_batch=len(ids),padded_tokens=ids.shape[1], | |
| vision_tiles=0 if inputs.get('spatial_shapes') is None else len(inputs['spatial_shapes']), | |
| input_tokens=int(attention.sum()),target_tokens=int((labels[:,1:]!=-100).sum()), | |
| supervised_loss=float(supervised.detach()),memory_loss=float(auxiliary.detach()), | |
| native_forward_seconds=forward_seconds,source_gradient_seconds=gradient_seconds, | |
| memory_aux_seconds=time.monotonic()-tick,auxiliary_feature_pairs=feature_count, | |
| source_autograd_traversals=0,training_chunks=0,persistent_training_sessions=0, | |
| ffn_gradient_source='native supervised backward', | |
| dnc_functional_read_loss=float(dnc_loss.detach()),graph_read_loss=float(graph_loss.detach()), | |
| memory_read_before=sum(read_before)/max(1,len(read_before)),memory_read_after=sum(read_after)/max(1,len(read_after)), | |
| native_forward=f'{native_forward.__module__}.{native_forward.__qualname__}') | |
| if events: | |
| self.last.update(weight_materialization_seconds=events[0].elapsed_time(events[1])/1000, | |
| native_forward_seconds=events[1].elapsed_time(events[2])/1000, | |
| source_gradient_seconds=events[2].elapsed_time(events[3])/1000, | |
| memory_aux_seconds=events[3].elapsed_time(events[4])/1000,timing_basis='CUDA events') | |
| return output | |
| class NativeMemoryCheckpoint(TrainerCallback): | |
| def __init__(self,model,resume=None):self.model=model;self.resume=resume;self.pending_rest=0.;self.gradient_logged=False | |
| def on_train_begin(self,args,state,control,**kwargs): | |
| self.model._native_sft_memory.gradient_scale=args.gradient_accumulation_steps | |
| # Trainer restores serialized cadence *after* computing it from the | |
| # current arguments. Honor the requested native save/log/eval schedule | |
| # on resume, without changing the optimizer or data position. | |
| if hasattr(state,'compute_steps'):state.compute_steps(args,state.max_steps) | |
| def on_step_begin(self,args,state,control,**kwargs): | |
| if self.pending_rest:time.sleep(self.pending_rest);self.pending_rest=0. | |
| self.tick=time.monotonic() | |
| if torch.cuda.is_available():torch.cuda.reset_peak_memory_stats() | |
| def on_pre_optimizer_step(self,args,state,control,**kwargs): | |
| if self.gradient_logged:return | |
| groups={} | |
| for name,p in self.model.named_parameters(): | |
| if not p.requires_grad:continue | |
| group=('vision' if '.vision_tower.' in name else 'projector' if '.multi_modal_projector.' in name | |
| else 'language' if '.language_model.' in name else 'titans' if name.startswith('memory.') | |
| else 'physical_ffn' if name.startswith('physical_memory.') else 'memory_readers') | |
| value=groups.setdefault(group,dict(parameters=0,with_gradient=0,squares=[])) | |
| value['parameters']+=p.numel() | |
| if p.grad is not None: | |
| if not torch.isfinite(p.grad).all():raise FloatingPointError('nonfinite LoRA gradient: '+name) | |
| value['with_gradient']+=p.numel();value['squares'].append(p.grad.detach().float().square().sum()) | |
| for value in groups.values(): | |
| terms=value.pop('squares');value['gradient_norm']=float(torch.stack(terms).sum().sqrt()) if terms else 0. | |
| (Path(args.output_dir)/f'gradient-audit-step-{state.global_step+1}.json').write_text(json.dumps(groups,indent=2)+'\n') | |
| components={} | |
| for prefix in ('memory_readers.controller.','memory_readers.attention.','memory_readers.visual.'): | |
| selected=[p for n,p in self.model.named_parameters() if n.startswith(prefix) and p.requires_grad] | |
| squares=[p.grad.detach().float().square().sum() for p in selected if p.grad is not None] | |
| components[prefix]=dict(parameters=sum(p.numel() for p in selected), | |
| with_gradient=sum(p.numel() for p in selected if p.grad is not None), | |
| gradient_norm=float(torch.stack(squares).sum().sqrt()) if squares else 0.) | |
| (Path(args.output_dir)/f'component-gradient-audit-step-{state.global_step+1}.json').write_text(json.dumps(components,indent=2)+'\n') | |
| self.gradient_logged=True | |
| def on_step_end(self,args,state,control,**kwargs): | |
| elapsed=time.monotonic()-self.tick | |
| value=dict(status='running',step=state.global_step,total_steps=state.max_steps, | |
| step_compute_seconds=elapsed,**self.model._native_sft_memory.last) | |
| if torch.cuda.is_available():value.update(peak_allocated_gib=torch.cuda.max_memory_allocated()/2**30, | |
| allocated_gib=torch.cuda.memory_allocated()/2**30,reserved_gib=torch.cuda.memory_reserved()/2**30) | |
| path=Path(args.output_dir)/'progress.json';temporary=path.with_suffix('.pending') | |
| temporary.write_text(json.dumps(value,ensure_ascii=False,indent=2)+'\n');temporary.replace(path) | |
| print(json.dumps(value,ensure_ascii=False),flush=True) | |
| self.pending_rest=elapsed*getattr(self.model.config,'native_sft_rest_ratio',1.) | |
| stop=os.environ.get('LFM2_SFT_PROBE_STOP') | |
| if stop is not None and state.global_step>=int(stop): | |
| control.should_training_stop=True;control.should_save=True | |
| def on_save(self,args,state,control,**kwargs): | |
| directory=Path(args.output_dir)/f'checkpoint-{state.global_step}' | |
| save_custom_lora(self.model,directory) | |
| receipt=dict(format='titans_dnc_without_vae',version=1,global_step=state.global_step) | |
| temporary=directory/'memory_training.pending' | |
| temporary.write_text(json.dumps(receipt,indent=2)+'\n');temporary.replace(directory/'memory_training.json') | |
| def on_prediction_step(self,args,state,control,**kwargs): | |
| if getattr(self.model.config,'native_memory_recall',None): | |
| seconds=self.model._native_sft_memory.last.get('forward_seconds',0.) | |
| time.sleep(seconds*getattr(self.model.config,'native_sft_rest_ratio',1.)) | |
| def on_evaluate(self,args,state,control,metrics=None,**kwargs): | |
| reports=self.model._native_sft_memory.eval_reports | |
| if not reports:return | |
| count=sum(r['physical_batch'] for r in reports) | |
| keys=['supervised_loss','dnc_recall_loss','fast_only_loss','physical_distillation_loss', | |
| 'read_policy_loss','memory_loss','read_policy_regret','dnc_memory_nll','physical_memory_nll', | |
| 'correct_memory_nll','empty_memory_nll','wrong_memory_nll', | |
| 'wrong_dnc_memory_nll','wrong_physical_memory_nll','physical_source_nll'] | |
| values={k:sum(r[k]*r['physical_batch'] for r in reports)/count | |
| for k in keys if all(r.get(k) is not None for r in reports)} | |
| value=dict(step=state.global_step,examples=count,**values, | |
| objective_version=reports[0].get('objective_version'), | |
| policy_temperatures=reports[0].get('policy_temperatures'), | |
| read_action_counts={str(i):sum(r.get('read_policy_actions',[]).count(i) for r in reports) for i in range(4)}, | |
| query_or_answer_given_to_writer=any(r['query_or_answer_given_to_writer'] for r in reports), | |
| outer_meta_gradients=any(r.get('outer_meta_gradients',False) for r in reports), | |
| evaluation_checkpointed_layers=max(r.get('evaluation_checkpointed_layers',0) for r in reports), | |
| peak_allocated_gib=max(r.get('peak_allocated_gib',0.) for r in reports), | |
| native_evaluation_loss=(metrics or {}).get('eval_loss')) | |
| for name in ('read_policy_statistics','restricted_read_policy_statistics'): | |
| if all(name in r for r in reports): | |
| value[name]={k:sum(r[name][k]*r['physical_batch'] for r in reports)/count for k in reports[0][name]} | |
| if all('per_example' in r for r in reports):value['per_example']=[x for r in reports for x in r['per_example']] | |
| (Path(args.output_dir)/f'memory-eval-step-{state.global_step}.json').write_text(json.dumps(value,indent=2)+'\n') | |
| print(json.dumps(dict(stage='heldout_memory_evaluation',**value)),flush=True) | |
| reports.clear() | |
| def on_train_end(self,args,state,control,**kwargs): | |
| save_custom_lora(self.model,args.output_dir) | |