Lecture 032 · 独立准备并核对预处理 · 实现代码
2 · 独立准备并核对预处理 · 实现代码
主线阅读与优先实验
真实CLIP输入/权重/分数、候选改动、四条件规则与独立关系推理
展开完整实现代码
# 当前项目中的独立材料:已存在则复用,缺失才从本讲原站准备。
from pathlib import Path
import io, zipfile, requests
_base = Path.cwd()
_folder = 'lab10-clip'
_sentinel = 'assets/chelsea-cat.png'
_choices = [_base/_folder, _base/'lecture03-labs'/_folder, _base]
_ready = next((p for p in _choices if (p/_sentinel).is_file()), None)
if _ready is None:
_url = 'https://codingai-lec03.pages.dev/course/materials/' + _folder + '.zip'
_response = requests.get(_url, timeout=60)
_response.raise_for_status()
_payload = _response.content
with zipfile.ZipFile(io.BytesIO(_payload)) as _archive:
_names = [i.filename for i in _archive.infolist() if not i.is_dir()]
_prefix = _folder + '/'
_prefixed = all(n.startswith(_prefix) for n in _names)
_destination = _base if _prefixed else _base/_folder
for _item in _archive.infolist():
_target = (_destination/_item.filename).resolve()
if not _target.is_relative_to(_destination.resolve()):
raise ValueError('材料包中有越界路径,停止解压。')
if _item.is_dir():
_target.mkdir(parents=True, exist_ok=True)
elif not _target.exists():
_target.parent.mkdir(parents=True, exist_ok=True)
_target.write_bytes(_archive.read(_item))
_ready = next((p for p in _choices if (p/_sentinel).is_file()), None)
assert _ready is not None, '请将本讲完整材料包解压到当前项目的 ' + _folder + '/;保留文件结构。'
LAB_ROOT = _ready
print('材料目录已就绪:', LAB_ROOT)
import json, hashlib, platform, sys
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
ROOT=LAB_ROOT
if not (ROOT/'assets').is_dir() and (ROOT/'lab10-clip'/'assets').is_dir():ROOT=ROOT/'lab10-clip'
assert (ROOT/'assets').is_dir(), '请解压本实验完整包,并在该目录或其父目录运行。'
OUT=ROOT/'runs/current'; OUT.mkdir(parents=True,exist_ok=True)
FIG=OUT/'figures'; FIG.mkdir(exist_ok=True)
plt.rcParams.update({'figure.dpi':120,'font.size':10,'axes.spines.top':False,'axes.spines.right':False})
BLUE,GOLD,GREEN,PURPLE='#002676','#E6A500','#00856A','#69458D'
def sha_file(path):return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def savefig(name):
if not plt.gcf().get_constrained_layout():plt.tight_layout()
plt.savefig(FIG/name,dpi=140,bbox_inches='tight');plt.show()
import torch
from PIL import Image
# 若宿主缺少固定预处理依赖,仅装入本次临时目录;不修改项目环境或下载模型权重。
import importlib.util, importlib.metadata, subprocess
try:
_transformers_version=importlib.metadata.version('transformers')
except importlib.metadata.PackageNotFoundError:
_transformers_version=None
if _transformers_version!='4.57.1':
_deps=ROOT/'_temporary_preprocessing_dependencies'
subprocess.check_call([sys.executable,'-m','pip','install','--quiet','--disable-pip-version-check',
'--no-cache-dir','--target',str(_deps),'transformers==4.57.1',
'numpy=='+np.__version__,'filelock>=3.20.3,<4'])
sys.path.insert(0,str(_deps))
importlib.invalidate_caches()
from transformers import CLIPProcessor,CLIPImageProcessor,AutoTokenizer
import transformers
assert transformers.__version__=='4.57.1', '请使用包内固定 requirements.txt。'
torch.set_num_threads(2)
provenance=json.loads((ROOT/'assets/provenance.json').read_text())
assert sha_file(ROOT/'assets/chelsea-cat.png')==provenance['image_sha256']
REVISION=provenance['revision']
local=ROOT/'.model-cache'/REVISION
config_local=ROOT/'model-config'
# small verified config files ship in the package; weights are downloaded only on request.
manifest=json.loads((config_local/'manifest.json').read_text())
for f,expected in manifest['sha256'].items():assert sha_file(config_local/f)==expected
clip_processor=CLIPProcessor(image_processor=CLIPImageProcessor.from_pretrained(config_local,local_files_only=True),tokenizer=AutoTokenizer.from_pretrained(config_local,local_files_only=True,use_fast=True))
photo=Image.open(ROOT/'assets/chelsea-cat.png').convert('RGB')
arms={'A_base':(['cat','dog','horse'],'a photo of a {}.'),'B_expand_only':(['cat','dog','horse','tiger','fox'],'a photo of a {}.'),'C_prompt_only':(['cat','dog','horse'],'a cropped photo of a {}.'),'D_remove_cat':(['dog','horse','tiger','fox'],'a photo of a {}.')}
image_inputs=clip_processor(images=photo,return_tensors='pt')
all_prompts=list(dict.fromkeys(template.format(name) for names,template in arms.values() for name in names))
text_inputs=clip_processor(text=all_prompts,padding=True,return_tensors='pt')
assert tuple(image_inputs['pixel_values'].shape)==(1,3,224,224)
assert clip_processor.image_processor.__class__.__name__=='CLIPImageProcessor'
assert clip_processor.tokenizer.__class__.__name__=='CLIPTokenizerFast'
preprocess_verification={'scope':'preprocessing only, no weights loaded','pixel_shape':list(image_inputs['pixel_values'].shape),'pixel_sha256':hashlib.sha256(image_inputs['pixel_values'].numpy().tobytes()).hexdigest(),'prompt_count':len(all_prompts),'tokenizer':clip_processor.tokenizer.__class__.__name__,'image_processor':clip_processor.image_processor.__class__.__name__,'transformers':transformers.__version__,'image_sha256':sha_file(ROOT/'assets/chelsea-cat.png'),'revision':REVISION}
historical_preprocess=json.loads((ROOT/'historical-v16/results/preprocessing-audit-20260918.json').read_text())
assert preprocess_verification['pixel_sha256']==historical_preprocess['pixel_values_sha256']
legacy_processor=CLIPProcessor.from_pretrained(config_local,local_files_only=True)
legacy_image=legacy_processor(images=photo,return_tensors='pt')
legacy_text=legacy_processor(text=all_prompts,padding=True,return_tensors='pt')
assert torch.equal(legacy_image['pixel_values'],image_inputs['pixel_values'])
assert all(torch.equal(legacy_text[k],text_inputs[k]) for k in text_inputs)
preprocess_verification['legacy_default_pixel_max_abs_difference']=float((legacy_image['pixel_values']-image_inputs['pixel_values']).abs().max())
preprocess_verification['all_prompt_token_tensors_equal']=True
preprocess_verification['token_ids_sha256']=hashlib.sha256(text_inputs['input_ids'].numpy().tobytes()).hexdigest()
(OUT/'preprocessing-verification.json').write_text(json.dumps(preprocess_verification,indent=2));print(json.dumps(preprocess_verification,indent=2))