Lecture 030 · 固定输入、来源与数据职责 · 实现代码
0 · 固定输入、来源与数据职责 · 实现代码
按问题继续
固定CNN与缓存特征、标签预算、kNN/linear和独立评价
展开完整实现代码
# 当前项目中的独立材料:已存在则复用,缺失才从本讲原站准备。
from pathlib import Path
import io, zipfile, urllib.request
_base = Path.cwd()
_folder = 'lab10-readout'
_sentinel = 'assets/target.npz'
_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'
_payload = urllib.request.urlopen(_url, timeout=60).read()
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, time
from pathlib import Path
from collections import OrderedDict
import numpy as np
import matplotlib.pyplot as plt
import torch
from torch import nn
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
import sklearn
ROOT=LAB_ROOT
if not (ROOT/'assets/target.npz').exists() and (ROOT/'lab10-readout/assets/target.npz').exists():
ROOT=ROOT/'lab10-readout'
assert (ROOT/'assets/target.npz').exists(), '请下载完整材料包并解压为项目中的 lab10-readout/;单独 ipynb 只供读取保存输出。'
OUT=ROOT/'runs/current'; OUT.mkdir(parents=True,exist_ok=True)
FIG=OUT/'figures'; FIG.mkdir(exist_ok=True)
torch.set_num_threads(2); torch.manual_seed(20260918)
torch.use_deterministic_algorithms(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()
provenance=json.loads((ROOT/'assets/provenance.json').read_text())
assert sha_file(ROOT/'assets/cnn-plain-state.pt')==provenance['source_checkpoint_sha256']
assert sha_file(ROOT/'assets/target.npz')==provenance['target_asset_sha256']
assert provenance['source_checkpoint_sha256']=='ba8b738cc67d4ec3cd83d75a5dbe8a70ec548377335ba0abaaa7c23cd14eeba3'
z=np.load(ROOT/'assets/target.npz',allow_pickle=False)
images=z['images']; digits=z['digit_labels']; sample_ids=z['official_train_ids']; split=z['split']
partition=np.random.default_rng(20260908).permutation(60000)
assert np.array_equal(sample_ids,partition[12000:12640])
assert not set(sample_ids)&set(partition[:12000])
assert not set(sample_ids)&set(partition[50000:])
assert len(set(sample_ids))==640
train=np.flatnonzero(split==0); val=np.flatnonzero(split==1); heldout=np.flatnonzero(split==2)
assert not set(train)&set(val) and not set(train)&set(heldout) and not set(val)&set(heldout)
x=torch.tensor(images,dtype=torch.float32)/255
shifted=torch.zeros_like(x); shifted[:,:,:,2:]=x[:,:,:,:-2]; x=shifted
y=(digits>=5).astype(np.int64)
fig,axes=plt.subplots(1,8,figsize=(12,2.3))
for i,ax in enumerate(axes):
ax.imshow(x[i,0],cmap='gray',vmin=0,vmax=1); ax.set_title(f'id {sample_ids[i]}\ndigit {digits[i]} → y={y[i]}',fontsize=9); ax.axis('off')
savefig('01-fixed-inputs.png')
print('Split sizes:',{'train':len(train),'validation':len(val),'heldout':len(heldout)})
print('SOURCE:',provenance['source_checkpoint_sha256'])
print('Checks passed: file identity; sample separation; preprocessing.')