<jupyter_start><jupyter_text>Классификация рукописных цифр с помощью TensorFlow ![](images/mnist.jpeg)Классическая задача распознавания рукописных цифр на основе набора данных [MNIST](http://yann.lecun.com/exdb/mnist/)  Постановка задачи- 60_000 изображений с цифрами- Отцентрированы- Черно-белые- Размер 28x28 пикселя  Простейшая однослойная нейросеть ![](images/one_layer.png) Изображение цифры, размером 28x28 уплощается в вектор размера 784 пикселей, который подается на вход однослойной нейросети. Каждый нейрон по отдельности проивзодит взвешенную сумму всех входных сигналов, добавляет некоторый bias-порог и пропускает результат через некоторую функцию активации. ![](images/perceptron_schematic.png)  Функция перцептрона$$Y = g(X.W + b) $$$$sigmoid(z_n) = \frac{1}{1 + e^{z_n}} $$$$softmax(z_n) = \frac{e^{z_n}}{||{e^z}||} $$ ![](images/activation_functions.png) Так как функции активации по определению возвращают числа от 0 до 1, то мы получаем, что фактически наш перцептрон возвращает вероятности принадлежности к классу. ![](images/eval_single_layer.png) ![](images/softmax-regression-vectorequation.png)  Тренировка сети$$H_{y'}(y) = - \sum{y'_i log(y_i)}$$y - оригинальный  векторy' - предсказанный  вектор ![](images/cross_entropy.png)  Практика 0- импортировать TF- переменные, константы, подстановки в TF- запуск вычислений<jupyter_code>import tensorflow as tf
tf.set_random_seed(0)<jupyter_output><empty_output><jupyter_text>Создание констант<jupyter_code>node1 = tf.constant(3.0)
node2 = tf.constant(4.0)
print(node1, node2)<jupyter_output>Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)<jupyter_text>Запуск вычислений<jupyter_code>sess = tf.InteractiveSession()
print(sess.run([node1, node2]))
node3 = tf.add(node1, node2)
print("node3:", node3)
print("sess.run(node3):", sess.run(node3))<jupyter_output>node3: Tensor("Add:0", shape=(), dtype=float32)
sess.run(node3): 7.0<jupyter_text>Создание подстановок<jupyter_code>a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b # переопределенный оператор для tf.add
print(sess.run(adder_node, {a: 3, b: 4.5}))
print(sess.run(adder_node, {a: [1, 3], b: [2, 4]}))<jupyter_output>7.5
[ 3.  7.]<jupyter_text>Создание переменных<jupyter_code>W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
print(linear_model)
init = tf.global_variables_initializer()
sess.run(init)
print(sess.run(linear_model, {x: [1, 2, 3, 4]}))
y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas) # функция потерь - или ошибки
print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))
# Поправим вручную потери линейного алгоритма.
fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))<jupyter_output>0.0<jupyter_text>Практика 1- импортировать TF- загрузить dataset- описать структуру сети- запустить обучение  Формулы для реализации$$Y = softmax(X.W + b) $$$$softmax(z_n) = \frac{e^{z_n}}{||{e^z}||} $$$$H_{y'}(y) = - \sum{y'_i log(y_i)}$$<jupyter_code># reset jupyter kernell to avoid variables duplications
%reset -sf

import tensorflow as tf
tf.set_random_seed(0)
tf.reset_default_graph()
with tf.name_scope('X'):
    X = tf.placeholder(tf.float32, [None, 784], name='X')
    x_image = tf.reshape(X, [-1, 28, 28, 1])
with tf.name_scope('weights'):
    W = tf.Variable(tf.zeros([784, 10]), name='weights')
with tf.name_scope('biases'):
    b = tf.Variable(tf.zeros([10]), name='biases')
    
tf.summary.image('input', x_image, 3)
tf.summary.histogram("weights", W)
tf.summary.histogram("biases", b)
with tf.name_scope('Wx_plus_b'):
    # Модель Y = X.W + b
    Y = tf.nn.softmax(tf.matmul(tf.reshape(X, [-1, 784]), W) + b, name='labels')

# Подстановка для корректных значений входных данных
Y_ = tf.placeholder(tf.float32, [None, 10], name='Y_')

with tf.name_scope('total'):
    # Функция потерь H = Sum(Y_ * log(Y))
    cross_entropy = -tf.reduce_sum(Y_ * tf.log(Y), name='xentropy')

with tf.name_scope('accuracy'):
    with tf.name_scope('correct_prediction'):
        # Доля верных ответов найденных в наборе
        is_correct = tf.equal(tf.argmax(Y,1), tf.argmax(Y_,1))
    with tf.name_scope('accuracy'):
        accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32), name='xentropy_mean')

tf.summary.scalar('accuracy', accuracy)
tf.summary.scalar('cross_entropy', cross_entropy)
with tf.name_scope('train'):
    # Оптимизируем функцию потерь меотодом градиентного спуска
    # 0.003 - это шаг градиента, гиперпараметр
    optimizer = tf.train.GradientDescentOptimizer(0.003)
    # Минимизируем потери
    train_step = optimizer.minimize(cross_entropy)
# Загружаем тренировочный датасет MNIST
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True, reshape=True)
def save_summary(sess, mnist, writer, merged, accuracy, i):
    if i % 10 == 0:
        test_data={X: mnist.test.images, Y_: mnist.test.labels}
        summary, a = sess.run([merged, accuracy], feed_dict=test_data)
        writer.add_summary(summary, i)
        if i % 200 == 0:
            print("Test: {}".format(a))
with tf.Session() as sess:
    merged = tf.summary.merge_all() # Merge all the summaries and write them out to
    writer = tf.summary.FileWriter("/tmp/tensorflow/one_layer", sess.graph)
    tf.global_variables_initializer().run()

    for i in range(1000):
        # загружаем набор изображений и меток классов
        batch_X, batch_Y = mnist.train.next_batch(100)
        train_data={X: batch_X, Y_: batch_Y}

        # train
        sess.run(train_step, feed_dict=train_data)
        
        save_summary(sess, mnist, writer, merged, accuracy, i)
        
    writer.close()
!ls /tmp/tensorflow/
def single_layer(mnist):
    X = tf.placeholder(tf.float32, [None, 784], name='X')
    W = tf.Variable(tf.zeros([784, 10]), name='weights')
    b = tf.Variable(tf.zeros([10]), name='biases')

    Y = tf.nn.softmax(tf.matmul(tf.reshape(X, [-1, 784]), W) + b, name='labels')
    Y_ = tf.placeholder(tf.float32, [None, 10], name='Y_')

    cross_entropy = -tf.reduce_sum(Y_ * tf.log(Y), name='xentropy')
    is_correct = tf.equal(tf.argmax(Y,1), tf.argmax(Y_,1))
    accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32), name='xentropy_mean')

    optimizer = tf.train.GradientDescentOptimizer(0.003)
    train_step = optimizer.minimize(cross_entropy)

    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in range(1000):
            batch_X, batch_Y = mnist.train.next_batch(100)
            train_data={X: batch_X, Y_: batch_Y}

            # train
            sess.run(train_step, feed_dict=train_data)

            if i % 100 == 0:
                test_data={X: mnist.test.images, Y_: mnist.test.labels}
                a, c = sess.run([accuracy, cross_entropy], feed_dict=test_data)
                print("Test: {}, {}".format(a, c))
single_layer(mnist)<jupyter_output>Test: 0.5440999865531921, 20443.087890625
Test: 0.8896999955177307, 4244.2392578125
Test: 0.8974999785423279, 3746.39013671875
Test: 0.9070000052452087, 3413.471435546875
Test: 0.9103000164031982, 3245.257080078125
Test: 0.9106000065803528, 3193.984375
Test: 0.9143000245094299, 3084.836669921875
Test: 0.9118000268936157, 3124.13232421875
Test: 0.9151999950408936, 3025.076416015625
Test: 0.9143999814987183, 3015.42431640625<jupyter_text>0.91![](images/tiny_success.jpg) ![](images/go_deeper.jpg)  Практика 2- Многослойная нейросеть- проблема переобучения- регуляризация  Многослойная сеть![](images/deep_neural_network.png) ![](images/deep_mnist.png) $$sigmoid(z_n) = \frac{1}{1 + e^{z_n}} $$![](images/sigmoid.png)<jupyter_code># reset jupyter kernell to avoid variables duplications
%reset -sf

import math
import tensorflow as tf
tf.set_random_seed(0)
tf.reset_default_graph()

# Загружаем тренировочный датасет MNIST
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True, reshape=True)
%matplotlib inline
import matplotlib.pyplot as plt

def draw(a, c):
    plt.figure(1, (15, 5))
    plt.subplot(121)
    plt.title('accuracy')
    plt.plot(a)

    plt.subplot(122)
    plt.title('cross_entropy')
    plt.plot(c)
    plt.show()
# Объявим размеры слоёв

layer_sizes = [28*28, 200, 100, 60, 30, 10]

W1 = tf.Variable(tf.truncated_normal([layer_sizes[0], layer_sizes[1]], stddev=0.1))
B1 = tf.Variable(tf.zeros(layer_sizes[1])) # 200

W2 = tf.Variable(tf.truncated_normal([layer_sizes[1], layer_sizes[2]], stddev=0.1))
B2 = tf.Variable(tf.zeros(layer_sizes[2])) # 100

W3 = tf.Variable(tf.truncated_normal([layer_sizes[2], layer_sizes[3]], stddev=0.1))
B3 = tf.Variable(tf.zeros(layer_sizes[3])) # 60

W4 = tf.Variable(tf.truncated_normal([layer_sizes[3], layer_sizes[4]], stddev=0.1))
B4 = tf.Variable(tf.zeros(layer_sizes[4])) # 30

W5 = tf.Variable(tf.truncated_normal([layer_sizes[4], layer_sizes[5]], stddev=0.1))
B5 = tf.Variable(tf.zeros(layer_sizes[5])) # 10
X =  tf.placeholder(tf.float32, [None, 784])

Y1 = tf.nn.sigmoid(tf.matmul(X, W1) + B1)
Y2 = tf.nn.sigmoid(tf.matmul(Y1, W2) + B2)
Y3 = tf.nn.sigmoid(tf.matmul(Y2, W3) + B3)
Y4 = tf.nn.sigmoid(tf.matmul(Y3, W4) + B4)

Ylogits = tf.matmul(Y4, W5) + B5
Y = tf.nn.softmax(Ylogits)
Y_ = tf.placeholder(tf.float32, [None, 10])
def train_deep(X, Y, Ylogits, Y_, epoches=1000):
    cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=Ylogits, labels=Y_)
    cross_entropy = tf.reduce_mean(cross_entropy)*100
    
    is_correct = tf.equal(tf.argmax(Y,1), tf.argmax(Y_,1))
    accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))

    learning_rate=0.003
    optimizer = tf.train.AdamOptimizer(learning_rate)
    train_step = optimizer.minimize(cross_entropy)

    accuracy_vis, cross_entropy_vis = [], []
    
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in range(epoches):
            batch_X, batch_Y = mnist.train.next_batch(100)
            train_data={X: batch_X, Y_: batch_Y}

            # train
            sess.run(train_step, feed_dict=train_data)

            if i % 10 == 0:
                test_data={X: mnist.test.images, Y_: mnist.test.labels}
                a, c = sess.run([accuracy, cross_entropy], feed_dict=test_data)
                accuracy_vis.append(a)
                cross_entropy_vis.append(c)
                if i % 100 == 0:
                    print("Test: {}, {}".format(a, c))
    return (accuracy_vis, cross_entropy_vis)
a, c = train_deep(X, Y, Ylogits, Y_)
draw(a,c)<jupyter_output><empty_output><jupyter_text>0.94???![](images/fry.png)  sigmoid + softmax + 5 layers- Качество классификации: 0.94- Медленно сходится- Есть резкие перепады при обучении- Нужна другая функция активации  Relu = Rectified Linear Unit![](images/relu.png)```pythontf.nn.relu(tf.matmul(X,W1)+B1)```<jupyter_code>X =  tf.placeholder(tf.float32, [None, 784])

Y1 = tf.nn.relu(tf.matmul(X, W1) + B1)
Y2 = tf.nn.relu(tf.matmul(Y1, W2) + B2)
Y3 = tf.nn.relu(tf.matmul(Y2, W3) + B3)
Y4 = tf.nn.relu(tf.matmul(Y3, W4) + B4)

Ylogits = tf.matmul(Y4, W5) + B5
Y = tf.nn.softmax(Ylogits)
Y_ = tf.placeholder(tf.float32, [None, 10])
a, c = train_deep(X, Y, Ylogits, Y_)
draw(a,c)<jupyter_output><empty_output><jupyter_text>relu + softmax + 5 layers- качество классификации 0.97- быстро сходится- относительно стабильно?? ![](images/fry.png)<jupyter_code>a, c = train_deep(X, Y, Ylogits, Y_, 5000)<jupyter_output>Test: 0.14419999718666077, 228.2206573486328
Test: 0.9193000197410583, 26.626399993896484
Test: 0.921999990940094, 26.062955856323242
Test: 0.9225000143051147, 24.187725067138672
Test: 0.9524000287055969, 15.917776107788086
Test: 0.9563999772071838, 13.650592803955078
Test: 0.9650999903678894, 11.619589805603027
Test: 0.9623000025749207, 12.328930854797363
Test: 0.9704999923706055, 9.90969467163086
Test: 0.968500018119812, 10.409608840942383
Test: 0.9639000296592712, 11.868613243103027
Test: 0.9728000164031982, 9.583634376525879
Test: 0.9699000120162964, 10.106823921203613
Test: 0.9704999923706055, 9.33514404296875
Test: 0.9678999781608582, 10.709968566894531
Test: 0.9690999984741211, 10.103565216064453
Test: 0.9688000082969666, 10.650016784667969
Test: 0.9746999740600586, 8.89111042022705
Test: 0.9740999937057495, 9.634437561035156
Test: 0.9733999967575073, 9.40545654296875
Test: 0.9735999703407288, 9.46670150756836
Test: 0.9736999869346619, 9.855304718017578
Test: 0.9754999876022339,[...]<jupyter_text>Шумы в кривой точности<jupyter_code>draw(a[50:], c[50:])<jupyter_output><empty_output><jupyter_text>Learning rate decay- замедлять скорость обучения по ходу обучения```max_learning_rate = 0.003min_learning_rate = 0.0001decay_speed = 2000.0learning_rate = min_learning_rate + (max_learning_rate - min_learning_rate) * math.exp(-i/decay_speed)```<jupyter_code>def train_lr_decay(X, Y, Ylogits, Y_, mnist, epoches=1000, verbose=100):
    cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=Ylogits, labels=Y_)
    cross_entropy = tf.reduce_mean(cross_entropy)*100
    
    is_correct = tf.equal(tf.argmax(Y,1), tf.argmax(Y_,1))
    accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))

    max_learning_rate = 0.003
    min_learning_rate = 0.0001
    decay_speed = 2000.0
    learning_rate = tf.placeholder(tf.float32)
    
    optimizer = tf.train.AdamOptimizer(learning_rate)
    train_step = optimizer.minimize(cross_entropy)

    accuracy_vis, cross_entropy_vis = [], []
    
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in range(epoches):
            batch_X, batch_Y = mnist.train.next_batch(100)
            
            lr = min_learning_rate + (max_learning_rate - min_learning_rate) * math.exp(-i/decay_speed)
            train_data={X: batch_X, Y_: batch_Y, learning_rate: lr}

            # train
            sess.run(train_step, feed_dict=train_data)

            if i % 10 == 0:
                test_data={X: mnist.test.images, Y_: mnist.test.labels}
                a, c = sess.run([accuracy, cross_entropy], feed_dict=test_data)
                accuracy_vis.append(a)
                cross_entropy_vis.append(c)
                if i % verbose == 0:
                    print("Test: {}, {}".format(a, c))
    return (accuracy_vis, cross_entropy_vis)
a, c = train_lr_decay(X, Y, Ylogits, Y_, mnist, 10000)<jupyter_output>Test: 0.1859000027179718, 227.35543823242188
Test: 0.9544000029563904, 14.740668296813965
Test: 0.9699000120162964, 9.963876724243164
Test: 0.9735999703407288, 9.058289527893066
Test: 0.9779999852180481, 7.636318206787109
Test: 0.9793999791145325, 7.169418811798096
Test: 0.9789000153541565, 7.904794216156006
Test: 0.9783999919891357, 7.831852912902832
Test: 0.9796000123023987, 8.334189414978027
Test: 0.9797999858856201, 8.294150352478027
Test: 0.9807000160217285, 8.431187629699707
Test: 0.9810000061988831, 8.371230125427246
Test: 0.9807999730110168, 8.59482479095459
Test: 0.9807000160217285, 8.825003623962402
Test: 0.98089998960495, 9.139716148376465
Test: 0.9807000160217285, 9.22861099243164
Test: 0.9807999730110168, 9.49561882019043
Test: 0.9811000227928162, 9.663046836853027
Test: 0.9805999994277954, 9.864192962646484
Test: 0.9810000061988831, 10.201659202575684<jupyter_text>Переобучение<jupyter_code>draw(a[50:], c[50:])<jupyter_output><empty_output><jupyter_text>Drop Out![](images/dropout.png) ```pythonpkeep = tf.placeholder(tf.float32)Y1d = tf.nn.dropout(Y1, pkeep)train_data={X: batch_X, Y_: batch_Y, learning_rate: lr, pkeep: 0.75}test_data={X: mnist.test.images, Y_: mnist.test.labels, pkeep: 1.0}```<jupyter_code>def train_lr_decay_dropout(X, Y, Ylogits, Y_, pkeep, mnist, epoches=1000):
    cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=Ylogits, labels=Y_)
    cross_entropy = tf.reduce_mean(cross_entropy)*100
    
    is_correct = tf.equal(tf.argmax(Y,1), tf.argmax(Y_,1))
    accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))

    max_learning_rate = 0.003
    min_learning_rate = 0.0001
    decay_speed = 2000.0
    learning_rate = tf.placeholder(tf.float32)
    
    optimizer = tf.train.AdamOptimizer(learning_rate)
    train_step = optimizer.minimize(cross_entropy)

    accuracy_vis, cross_entropy_vis = [], []
    
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in range(epoches):
            batch_X, batch_Y = mnist.train.next_batch(100)
            
            lr = min_learning_rate + (max_learning_rate - min_learning_rate) * math.exp(-i/decay_speed)
            train_data={X: batch_X, Y_: batch_Y, learning_rate: lr, pkeep: 0.75}

            # train
            sess.run(train_step, feed_dict=train_data)

            if i % 10 == 0:
                test_data={X: mnist.test.images, Y_: mnist.test.labels, pkeep: 1.0}
                a, c = sess.run([accuracy, cross_entropy], feed_dict=test_data)
                accuracy_vis.append(a)
                cross_entropy_vis.append(c)
                if i % 500 == 0:
                    print("Test: {}, {}".format(a, c))
    return (accuracy_vis, cross_entropy_vis)
pkeep = tf.placeholder(tf.float32)

X =  tf.placeholder(tf.float32, [None, 784])

Y1 = tf.nn.dropout(tf.nn.relu(tf.matmul(X, W1) + B1), pkeep)
Y2 = tf.nn.dropout(tf.nn.relu(tf.matmul(Y1, W2) + B2), pkeep)
Y3 = tf.nn.dropout(tf.nn.relu(tf.matmul(Y2, W3) + B3), pkeep)
Y4 = tf.nn.dropout(tf.nn.relu(tf.matmul(Y3, W4) + B4), pkeep)

Ylogits = tf.matmul(Y4, W5) + B5
Y = tf.nn.softmax(Ylogits)
Y_ = tf.placeholder(tf.float32, [None, 10])
a, c = train_lr_decay_dropout(X, Y, Ylogits, Y_, pkeep, mnist, 10000)
draw(a[50:], c[50:])<jupyter_output><empty_output><jupyter_text>Резюме- 5 слоёв- relu & softmax- learning rate decay- dropout  0.98![](images/good_success.jpg)  Свёрточная нейросеть ![](images/common_cnn_design.jpg) ![](images/1.png) ![](images/2.png) ![](images/3.png) ![](images/conv_layers.png)<jupyter_code>mnist = input_data.read_data_sets("MNIST_data/", one_hot=True, reshape=False)
X = tf.placeholder(tf.float32, [None, 28, 28, 1])
Y_ = tf.placeholder(tf.float32, [None, 10])
lr = tf.placeholder(tf.float32)

K = 4  # first convolutional layer output depth
L = 8  # second convolutional layer output depth
M = 12  # third convolutional layer
N = 200  # fully connected layer
# сверточные слои
W1 = tf.Variable(tf.truncated_normal([5, 5, 1, K], stddev=0.1))
B1 = tf.Variable(tf.ones([K])/10)
W2 = tf.Variable(tf.truncated_normal([5, 5, K, L], stddev=0.1))
B2 = tf.Variable(tf.ones([L])/10)
W3 = tf.Variable(tf.truncated_normal([4, 4, L, M], stddev=0.1))
B3 = tf.Variable(tf.ones([M])/10)
# полносвязный слой
W4 = tf.Variable(tf.truncated_normal([7 * 7 * M, N], stddev=0.1))
B4 = tf.Variable(tf.ones([N])/10)
W5 = tf.Variable(tf.truncated_normal([N, 10], stddev=0.1))
B5 = tf.Variable(tf.ones([10])/10)
stride = 1  # output is 28x28
Y1 = tf.nn.relu(tf.nn.conv2d(X, W1, strides=[1, stride, stride, 1], padding='SAME') + B1)
stride = 2  # output is 14x14
Y2 = tf.nn.relu(tf.nn.conv2d(Y1, W2, strides=[1, stride, stride, 1], padding='SAME') + B2)
stride = 2  # output is 7x7
Y3 = tf.nn.relu(tf.nn.conv2d(Y2, W3, strides=[1, stride, stride, 1], padding='SAME') + B3)

# меняем размерность выхода из последнего сверточного слоая в полносвязный слой
YY = tf.reshape(Y3, shape=[-1, 7 * 7 * M])

Y4 = tf.nn.relu(tf.matmul(YY, W4) + B4)
Ylogits = tf.matmul(Y4, W5) + B5
Y = tf.nn.softmax(Ylogits)
a, c = train_lr_decay(X, Y, Ylogits, Y_, mnist, epoches=1000, verbose=100)
draw(a, c)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14711709423418848, 'fraction_numerical': 0.12039624079248158, 'mean_word_length': 4.381629305631493, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 50, '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': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '357219', 'license': 'Apache-2.0', 'n_tokens_mistral': 9674, 'n_tokens_neox': 8376, 'n_words': 1437, 'path': 'main.ipynb', 'repo_name': 'mlomsk/mnist_workshop', 'size': '19685'}