Lecture 031 · 没有类别标签,词典到底学了什么? · 实现代码
1 · 没有类别标签,词典到底学了什么? · 实现代码
按问题继续
真实MNIST patch的Lloyd拟合、词典中心、保留误差与历史输出
展开完整实现代码
# Actual Lloyd updates on image patches. Labels are deliberately absent from fit().
N_WORDS=16
PATCH=7
patches=x.reshape(640,1,4,PATCH,4,PATCH).transpose(0,2,4,1,3,5).reshape(640,16,PATCH*PATCH)
fit_patches=patches[train].reshape(-1,PATCH*PATCH)
fit_patches=fit_patches[fit_patches.mean(1)>.01].astype(np.float64)
def fit_lloyd_unlabeled(a,k,seed=20260918,max_iter=80):
rng=np.random.default_rng(seed)
centers=a[rng.choice(len(a),k,replace=False)].copy()
trace=[]
for iteration in range(max_iter):
distances=((a[:,None,:]-centers[None,:,:])**2).sum(2)
assignments=distances.argmin(1)
new=np.stack([a[assignments==j].mean(0) if np.any(assignments==j) else centers[j] for j in range(k)])
new_distances=((a[:,None,:]-new[None,:,:])**2).sum(2)
sse=float(new_distances.min(1).sum())
trace.append(sse)
if np.allclose(new,centers,rtol=0,atol=1e-10):
centers=new; break
centers=new
assert np.all(np.diff(trace)<=1e-7)
return centers,trace
centers,trace=fit_lloyd_unlabeled(fit_patches,N_WORDS)
frozen_centers=centers.copy()
test_patches=patches[heldout].reshape(-1,49)
test_dist=((test_patches[:,None,:]-frozen_centers[None,:,:])**2).sum(2)
test_assignment=test_dist.argmin(1)
assert np.array_equal(centers,frozen_centers)
kmeans_result={'identity':'L-A true MNIST pixel-patch Lloyd fit','labels_used_in_fit':False,
'patch_shape':[7,7],'n_words':N_WORDS,'train_images':len(train),'train_nonblank_patches':len(fit_patches),
'fit_sample_ids':sample_ids[train].tolist(),'evaluation_sample_ids':sample_ids[heldout].tolist(),
'train_sse_trace':trace,'heldout_mean_squared_pixel_error':float(test_dist.min(1).mean()/49),
'warning':'train excludes nearblank patches; heldout covers all patches, so these objectives are not identically weighted'}
np.savez_compressed(OUT/'kmeans-centers.npz',centers=centers)
(OUT/'kmeans-results.json').write_text(json.dumps(kmeans_result,indent=2))
fig=plt.figure(figsize=(12,4.4),layout='constrained')
outer=fig.add_gridspec(1,2,width_ratios=[1,1.3],wspace=.12)
grid=outer[0].subgridspec(int(np.ceil(N_WORDS/4)),4,wspace=.06,hspace=.12)
for j,c in enumerate(centers):
ax=fig.add_subplot(grid[j//4,j%4]);ax.imshow(c.reshape(7,7),cmap='gray',vmin=0,vmax=1);ax.set_title(f'word {j}',fontsize=8);ax.axis('off')
ax=fig.add_subplot(outer[1]);ax.plot(np.arange(1,len(trace)+1),trace,color=BLUE,marker='.',ms=4)
ax.set(xlabel='Lloyd iteration',ylabel='Training sum of squared distances',title='Real image patches; no digit labels')
savefig('02-unlabeled-fit.png')
print('Fitted nonblank patches:',len(fit_patches),'iterations:',len(trace))
print('Held-out pixel reconstruction MSE:',kmeans_result['heldout_mean_squared_pixel_error'])