<jupyter_start><jupyter_text>Notebook to run the model using pure python.Template for the C implementation of the model.<jupyter_code>from typing import Iterable, Dict

import numpy as np
weights = {}
for i in range(3):
    base_name = f"weights/conv{i}_{{}}.npy"
    weights[f"conv{i}_kernel"] = np.load(base_name.format("kernel"))
    weights[f"conv{i}_bias"] = np.load(base_name.format("bias"))
    
weights["dense_kernel"] = np.load("weights/dense_kernel.npy")
weights["dense_bias"] = np.load("weights/dense_bias.npy")
def apply_2x2_conv_kernels(x: np.ndarray, kernels: np.ndarray) -> np.ndarray:
    # we only need 2x2, no padding :D
    out_rows = x.shape[0] - 1
    out_cols = x.shape[1] - 1
    out = np.zeros((out_rows, out_cols, kernels.shape[-1]))
    
    for row in range(out_rows):
        for col in range(out_cols):
            sliding_window = x[row:row+2, col:col+2, :]
            out[row, col, :] = (sliding_window[...,np.newaxis] * kernels).sum(axis=(0,1,2))
    return out

            
def apply_full_2x2_conv_layer(x: np.ndarray, kernel: np.ndarray,
                              bias: np.ndarray) -> np.ndarray:

    convolved = apply_2x2_conv_kernels(x, kernel)
    with_bias = convolved + bias
    
    # apply relu
    return np.maximum(with_bias, 0)


def apply_conv_layer_stack(x: np.ndarray, kernels: Iterable[np.ndarray],
                           biases: Iterable[np.ndarray]) -> np.ndarray:
    for kernel, bias in zip(kernels, biases):
        x = apply_full_2x2_conv_layer(x, kernel, bias)
    return x


def apply_max_pool(x: np.ndarray) -> np.ndarray:
    return np.max(x, axis=(0,1))


def softmax(x: np.ndarray) -> np.ndarray:
    e = np.exp(x)
    return e / e.sum()


def apply_dense_output_layer(x: np.ndarray, kernel: np.ndarray,
                             bias: np.ndarray) -> np.ndarray:
    x = x[:,np.newaxis]
    out = ((kernel.T)@x)[:,0] + bias
    return softmax(out)


def apply_full_model(x: np.ndarray, weights: Dict[str, np.ndarray]):
    x = apply_conv_layer_stack(
        x,
        [weights[f"conv{i}_kernel"] for i in range(3)],
        [weights[f"conv{i}_bias"] for i in range(3)]
    )
    x = apply_max_pool(x)
    x = apply_dense_output_layer(
        x,
        kernel=weights["dense_kernel"],
        bias=weights["dense_bias"]
    )
    return x
test_input = np.array([
    [1,1,0,0,0],
    [0,0,0,0,0],
    [0,0,1,0,0],
    [0,0,1,1,0],
    [0,0,1,0,1]
    ])[..., np.newaxis]
apply_2x2_conv_kernels(test_input, weights["conv0_kernel"])[0,0,0]
apply_2x2_conv_kernels(test_input, weights["conv0_kernel"])[2,1,3]
res = apply_full_2x2_conv_layer(test_input, weights["conv0_kernel"], weights["conv0_bias"])
print(res[0,0,0])
print(res[2,1,3])
res = apply_conv_layer_stack(
        test_input,
        [weights[f"conv{i}_kernel"] for i in range(3)],
        [weights[f"conv{i}_bias"] for i in range(3)]
    )
print("res[0,0,0]:", res[0,0,0])
print("res[0,1,0]:", res[0, 1, 10])

res_pool = apply_max_pool(res)
res_pool
weights["conv0_kernel"][0,1,0,2]
for i in range(3):
    print(f"conv{i}")
    print(repr(weights[f"conv{i}_kernel"].flatten()))
    print(repr(weights[f"conv{i}_bias"].flatten()))
    print()
weights["dense_kernel"].T.flatten()
weights.keys()
weights["dense_bias"].flatten()
apply_full_model(
    test_input,
    weights
)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1648186994306263, 'fraction_numerical': 0.02846868444710818, 'mean_word_length': 3.462566844919786, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '196803', 'license': 'MIT', 'n_tokens_mistral': 1337, 'n_tokens_neox': 1299, 'n_words': 232, 'path': 'cnn/run_model.ipynb', 'repo_name': 'PaulKlinger/cnn_card', 'size': '3337'}