Close Enough 0% ◫Browse AaGlossary
4.6
Training

Learning the codebooks

So far the codebooks were given. Learning them is the easy half, because the decoder is linear. The trap is that free codebooks have a great many parameters.

~2 min

1The decoder update is least squares

Fix the codes for NN training vectors. Build a sparse N×MKN \times MK matrix BB with one 1 in each of the MM blocks of every row, marking the entry that row uses. Stack all codebooks into one MK×dMK \times d matrix CC.

The reconstruction of the whole training set is then X^=B C\kdec{\hat X} = B\,\kbook{C}, and the best codebooks for those codes solve a plain least-squares problem:

C⋆  =  arg⁡min⁡C∥X−B C∥F2  =  (B⊤B)−1B⊤X \kbook{C^\star} \;=\; \arg\min_C \lVert \kvec{X} - B\,C \rVert_F^2 \;=\; (B^\top B)^{-1} B^\top X
Alternate: encode, then solve for the codebooksnumpy
def update_codebooks(X, codes, K):
    N, M = codes.shape
    B = np.zeros((N, M * K))                  # sparse in practice
    B[np.arange(N)[:, None], np.arange(M) * K + codes] = 1
    C = np.linalg.lstsq(B, X, rcond=None)[0]  # (M*K, d)
    return np.split(C, M)

codebooks = rq_train(X)                       # the starting point matters a lot
for epoch in range(10):
    codes = encode(X, codebooks)              # beam / ICM: expensive
    codebooks = update_codebooks(X, codes, K) # least squares: cheap

This is k-means with a different assignment step.

Training inherits the encoder’s cost.

Every epoch re-encodes the whole training set with the expensive encoder. That is why LSQ-style methods spent much of their engineering on fast GPU encoding.

2The overfitting trap

Three defences, in order of preference:

  1. More data than parameters. Marginalia has 300 million vectors to train on; the experiment here had 63,120, about one sample per ten parameters.
  2. Regularise. A ridge term in the least-squares solve keeps rarely used codewords from chasing noise.
  3. Always score on held-out vectors. It is the only way to notice.