<jupyter_start><jupyter_text>Traffic Sign Classification build a model for classifying traffic signs  Dataset The dataset we’ll be using to train our own custom traffic sign classifier is the German Traffic Sign Benchmark https://www.kaggle.com/valentynsichkar/traffic-signs-preprocessed  About the datasetThe German Traffic Sign Benchmark is a multi-class, single-image classification challenge held at the International Joint Conference on Neural Networks (IJCNN) 2011. We cordially invite researchers from relevant fields to participate: The competition is designed to allow for participation without special domain knowledge. Our benchmark has the following properties:* Single-image, multi-class classification problem* More than 40 classes* More than 50,000 images in total* Large, lifelike database  Credits* [https://benchmark.ini.rub.de/](https://benchmark.ini.rub.de/)<jupyter_code>!python3 --version
import os
import zipfile
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras import optimizers

from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.preprocessing.image import load_img

from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPool2D, MaxPooling2D, AvgPool2D, BatchNormalization, Reshape

from sklearn.model_selection import train_test_split
import tqdm
import shutil

import numpy as np
import pandas as pd
from pathlib import Path

import matplotlib.pyplot as plt

%matplotlib inline<jupyter_output><empty_output><jupyter_text>Download the dataset from source<jupyter_code>!wget https://d17h27t6h515a5.cloudfront.net/topher/2017/February/5898cd6f_traffic-signs-data/traffic-signs-data.zip
!ls
!du -sh traffic-signs-data.zip<jupyter_output>118M	traffic-signs-data.zip<jupyter_text>Load labels<jupyter_code>!wget https://gist.githubusercontent.com/ceccode/119e37bcc2320819b7445c1090c0fe06/raw/0e3801a7c09091d9b9200ac454a4ba99203652c8/labels.csv
!ls<jupyter_output>labels.csv  sample_data  traffic-signs-data.zip<jupyter_text>Data preparation and data clearning Extract the dataset<jupyter_code>zip_ref = zipfile.ZipFile("./traffic-signs-data.zip", 'r')
zip_ref.extractall("../tmp/traffic-signs")
zip_ref.close()
BASE_PATH = '../tmp/traffic-signs/'
shutil.copyfile('./labels.csv', BASE_PATH + 'labels_name.csv') 

shutil.copyfile('./labels.csv', BASE_PATH + 'labels_train.csv')
shutil.copyfile('./labels.csv', BASE_PATH + 'labels_valid.csv')
shutil.copyfile('./labels.csv', BASE_PATH + 'labels_test.csv')
label_df = pd.read_csv(BASE_PATH + 'labels_name.csv')
label_df
import pickle
from PIL import Image
# load the database
training_file =  BASE_PATH + "train.p"
validation_file= BASE_PATH + "valid.p"
testing_file =   BASE_PATH + "test.p"

with open(training_file, mode='rb') as f:
    train = pickle.load(f)
with open(validation_file, mode='rb') as f:
    valid = pickle.load(f)
with open(testing_file, mode='rb') as f:
    test = pickle.load(f)
os.makedirs(BASE_PATH + 'train', exist_ok=True)
os.makedirs(BASE_PATH + 'valid', exist_ok=True)
os.makedirs(BASE_PATH + 'test', exist_ok=True)<jupyter_output><empty_output><jupyter_text>Move image file in test / train / valid<jupyter_code>train['features']
train['labels']<jupyter_output><empty_output><jupyter_text>Write images and labels<jupyter_code>with open(BASE_PATH+'labels_train.csv', 'w') as labels:
    # -- TRAINING --
    counter = 0
    for f,l in zip(train['features'], train['labels']):
        # write f to a jpg
        relname = "train/{name}.png".format(name=counter)
        fname = "{root}".format(root=BASE_PATH) + relname
        im = Image.fromarray(f)
        im.save(fname)
        # write l to labels.csv
        labels.write("{file},{label}\n".format(file=relname, label=l))
        counter += 1

with open(BASE_PATH+'labels_valid.csv', 'w') as labels:   
    counter = 0 
    for f,l in zip(valid['features'], valid['labels']):
        # write f to a jpg
        relname = "valid/{name}.png".format(name=counter)
        fname = "{root}".format(root=BASE_PATH) + relname
        im = Image.fromarray(f)
        im.save(fname)
        # write l to labels.csv
        labels.write("{file},{label}\n".format(file=relname, label=l))
        counter += 1

with open(BASE_PATH+'labels_test.csv', 'w') as labels:    
    counter = 0      
    # -- TEST --
    for f,l in zip(test['features'], test['labels']):
        # write f to a jpg
        relname = "test/{name}.png".format(name=counter)
        fname = "{root}".format(root=BASE_PATH) + relname
        im = Image.fromarray(f)
        im.save(fname)
        # write l to labels.csv
        labels.write("{file},{label}\n".format(file=relname, label=l))
        counter += 1<jupyter_output><empty_output><jupyter_text>Show some images<jupyter_code>name_1 = 'train/100.png'
image_fullname = f'{BASE_PATH}{name_1}'
load_img(image_fullname)
name_2 = 'train/10040.png'
image_fullname = f'{BASE_PATH}{name_2}'
load_img(image_fullname)
img = load_img(image_fullname)
x = np.array(img)
x.shape<jupyter_output><empty_output><jupyter_text>Train and Valid dataset<jupyter_code>df_train = pd.read_csv(BASE_PATH + 'labels_train.csv', names=["path", "class"])
df_train.head()
df_train.isnull().sum()
df_train["class"].value_counts()
df_train['class'].value_counts().plot(kind='bar')
plt.show()  # density=False would make counts<jupyter_output><empty_output><jupyter_text>The top class (Speed limit 50km) has over 3000 examples while the least represented class ("roundabout mandatory" end "End of no passing") has under 300 examples.<jupyter_code>df_valid = pd.read_csv(BASE_PATH + 'labels_valid.csv', names=["path", "class"])
df_valid.head()
train_dir = os.path.join(BASE_PATH, 'train')
valid_dir = os.path.join(BASE_PATH, 'valid')
test_dir = os.path.join(BASE_PATH, 'test')<jupyter_output><empty_output><jupyter_text>Train folder has 43 different folders named from 0 to 42. Each folder contains images from the respective class.<jupyter_code>for i in range(43):
  os.makedirs(train_dir + "/" +  str(i), exist_ok=True)
  os.makedirs(valid_dir + "/" +  str(i), exist_ok=True)
for i in range(len(df_train)) :
  source = BASE_PATH + df_train.loc[i, 'path']
  destination = train_dir + '/' + str(df_train.loc[i, 'class'])
  if (os.path.exists(source)):
    shutil.move(source, destination)


for i in range(len(df_valid)) :
  source = BASE_PATH + df_valid.loc[i, 'path']
  destination = valid_dir + '/' + str(df_valid.loc[i, 'class'])
  if (os.path.exists(source)):
    shutil.move(source, destination)<jupyter_output><empty_output><jupyter_text>Model Creation<jupyter_code>NUM_CLASSES = 43

DENSE_LAYER_ACTIVATION = 'softmax'
LOSS_METRICS = ['accuracy']

LEARNING_RATE = 0.001

NUM_EPOCHS = 10

STEPS_PER_EPOCH_TRAINING = 100
STEPS_PER_EPOCH_VALIDATION = 50

BATCH_SIZE_TRAINING = 100
BATCH_SIZE_VALIDATION = 50
model = models.Sequential()

model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))

model.add(MaxPooling2D((2, 2)))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(43, activation=DENSE_LAYER_ACTIVATION))<jupyter_output><empty_output><jupyter_text>Optimizer<jupyter_code>optimizer = keras.optimizers.Adam(learning_rate=LEARNING_RATE)

loss = keras.losses.CategoricalCrossentropy(from_logits=True)
model.compile(optimizer=optimizer, loss=loss, metrics=['accuracy'])
model.summary()<jupyter_output>Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 30, 30, 32)        896       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 15, 15, 32)       0         
 )                                                               
                                                                 
 flatten (Flatten)           (None, 7200)              0         
                                                                 
 dense (Dense)               (None, 128)               921728    
                                                                 
 dense_1 (Dense)             (None, 43)                5547      
                                                                 
========================================================[...]<jupyter_text>Generators and Training<jupyter_code>train_gen = ImageDataGenerator(rescale=1./255)

train_ds = train_gen.flow_from_directory(
    train_dir,
    target_size=(32, 32),
    class_mode="categorical",
    batch_size=BATCH_SIZE_TRAINING,
    shuffle=True
)
train_ds.class_indices
val_gen = ImageDataGenerator(rescale=1./255)

val_ds = val_gen.flow_from_directory(
    valid_dir,
    target_size=(32, 32),
    class_mode="categorical",
    batch_size=BATCH_SIZE_VALIDATION,
    shuffle=True
)
val_ds.class_indices
history = model.fit(
    train_ds,
    steps_per_epoch=STEPS_PER_EPOCH_TRAINING, # images = batch_size * steps
    epochs=NUM_EPOCHS,
    validation_data=val_ds,
    validation_steps=STEPS_PER_EPOCH_VALIDATION # images = batch_size * steps
)<jupyter_output>Epoch 1/10<jupyter_text>Model metrics<jupyter_code># list all data in history
print(history.history.keys())
acc = history.history['accuracy'] #training acc
val_acc = history.history['val_accuracy'] #validation acc
loss = history.history['loss']
val_loss = history.history['val_loss']<jupyter_output><empty_output><jupyter_text>Accurancy median<jupyter_code>acc_median = np.median(acc)
acc_median<jupyter_output><empty_output><jupyter_text>Loss std<jupyter_code>loss_std = np.std(loss)
loss_std
# summarize history for accuracy
plt.plot(acc)
plt.plot(val_acc)
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()

# summarize history for loss
plt.plot(loss)
plt.plot(val_loss)
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()<jupyter_output><empty_output><jupyter_text>Data augmentation Training a model with augmentations<jupyter_code>datagen = ImageDataGenerator(
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.1,
    zoom_range=0.1,
    horizontal_flip=True,
    fill_mode='nearest')
from tensorflow.keras.preprocessing import image

fnames = [os.path.join(train_dir+"/10", fname) for
    fname in os.listdir(train_dir+"/10")]

img_path = fnames[0]

img = image.load_img(img_path, target_size=(150, 150))

x = image.img_to_array(img)
x = x.reshape((1,) + x.shape)

i=0
for batch in datagen.flow(x, batch_size=1):
    plt.figure(i)
    imgplot = plt.imshow(image.array_to_img(batch[0]))
    i += 1
    if i % 4 == 0:
        break

plt.show()
train_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(train_dir,
                                                    target_size=(32, 32), 
                                                    class_mode="categorical",
                                                    batch_size=BATCH_SIZE_TRAINING)

validation_generator = test_datagen.flow_from_directory(
    valid_dir,
    target_size=(32, 32),
    class_mode="categorical",
    batch_size=BATCH_SIZE_VALIDATION)<jupyter_output>Found 4410 images belonging to 43 classes.<jupyter_text>Model fitting (augmented) and Accuracy/Loss Evaluation<jupyter_code>history = model.fit(
    train_generator,
    steps_per_epoch=STEPS_PER_EPOCH_TRAINING,
    epochs=NUM_EPOCHS,
    validation_data=validation_generator,
    validation_steps=STEPS_PER_EPOCH_VALIDATION)<jupyter_output>Epoch 1/10
100/100 [==============================] - 10s 102ms/step - loss: 3.1433 - accuracy: 0.2011 - val_loss: 1.8402 - val_accuracy: 0.4844
Epoch 2/10
100/100 [==============================] - 10s 102ms/step - loss: 2.4699 - accuracy: 0.2842 - val_loss: 1.7281 - val_accuracy: 0.5040
Epoch 3/10
100/100 [==============================] - 10s 102ms/step - loss: 2.2947 - accuracy: 0.3336 - val_loss: 1.6555 - val_accuracy: 0.5316
Epoch 4/10
100/100 [==============================] - 10s 102ms/step - loss: 2.1741 - accuracy: 0.3639 - val_loss: 1.5909 - val_accuracy: 0.5380
Epoch 5/10
100/100 [==============================] - 10s 102ms/step - loss: 2.0698 - accuracy: 0.3886 - val_loss: 1.5962 - val_accuracy: 0.5640
Epoch 6/10
100/100 [==============================] - 10s 102ms/step - loss: 2.0034 - accuracy: 0.4002 - val_loss: 1.5952 - val_accuracy: 0.5768
Epoch 7/10
100/100 [==============================] - 10s 104ms/step - loss: 1.9083 - accuracy: 0.4235 - val_loss: 1.6203 - val_ac[...]<jupyter_text>Model metrics<jupyter_code>acc = history.history['accuracy'] #training acc
val_acc = history.history['val_accuracy'] #validation acc
loss = history.history['loss']
val_loss = history.history['val_loss']<jupyter_output><empty_output><jupyter_text>Accurancy median<jupyter_code>acc_median = np.median(acc)
acc_median<jupyter_output><empty_output><jupyter_text>Loss std<jupyter_code>loss_std = np.std(loss)
loss_std
# summarize history for accuracy
plt.plot(acc)
plt.plot(val_acc)
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()

# summarize history for loss
plt.plot(loss)
plt.plot(val_loss)
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()<jupyter_output><empty_output><jupyter_text>Export model<jupyter_code>MODEL_PATH = BASE_PATH + 'traffic-sign-model.h5'

tf.keras.models.save_model(
    model,
    MODEL_PATH,
    overwrite=True,
    include_optimizer=True,
    save_format=None,
    signatures=None,
    options=None,
    save_traces=True,
)<jupyter_output><empty_output><jupyter_text>Load Model and Evaluate<jupyter_code>model_loaded = keras.models.load_model(MODEL_PATH)
test_gen = ImageDataGenerator(rescale=1./255)

test_ds = test_gen.flow_from_directory(
    train_dir,
    target_size=(32, 32),
    batch_size=32,
    shuffle=False
)

model_loaded.evaluate(test_ds)<jupyter_output>Found 34799 images belonging to 43 classes.
   5/1088 [..............................] - ETA: 13s - loss: 1.1741 - accuracy: 0.5437<jupyter_text>Predict<jupyter_code>path = test_dir + '/100.png' #Speed limit (30km/h)
img = load_img(path, target_size=(32, 32))

import numpy as np
x = np.array(img)
X = np.array([x])

X.shape
def preprocess_input(x):
    return x / 255.0

X = preprocess_input(X)
pred = model_loaded.predict(X)
pred
classes = [
  "Speed limit (20km/h)",
  "Speed limit (30km/h)",
  "Speed limit (50km/h)",
  "Speed limit (60km/h)",
  "Speed limit (70km/h)",
  "Speed limit (80km/h)",
  "End of speed limit (80km/h)",
  "Speed limit (100km/h)",
  "Speed limit (120km/h)",
  "No passing",
  "No passing for vehicles over 3.5 metric tons",
  "Right-of-way at the next intersection",
  "Priority road",
  "Yield",
  "Stop",
  "No vehicles",
  "Vehicles over 3.5 metric tons prohibited",
  "No entry",
  "General caution",
  "Dangerous curve to the left",
  "Dangerous curve to the right",
  "Double curve",
  "Bumpy road",
  "Slippery road",
  "Road narrows on the right",
  "Road work",
  "Traffic signals",
  "Pedestrians",
  "Children crossing",
  "Bicycles crossing",
  "Beware of ice/snow",
  "Wild animals crossing",
  "End of all speed and passing limits",
  "Turn right ahead",
  "Turn left ahead",
  "Ahead only",
  "Go straight or right",
  "Go straight or left",
  "Keep right",
  "Keep left",
  "Roundabout mandatory",
  "End of no passing",
  "End of no passing by vehicles over 3.5 metric tons"
]
dict(zip(classes, pred[0]))<jupyter_output><empty_output><jupyter_text>Speed limit (30km/h)  Convert the model for use with TensorFlow lite<jupyter_code># Convert the model.
converter = tf.lite.TFLiteConverter.from_keras_model(model_loaded)
tflite_model = converter.convert()

# Save the model.
MODEL_TFLITE_PATH = BASE_PATH + 'traffic-sign-model.tflite'

with open(MODEL_TFLITE_PATH, 'wb') as f:
  f.write(tflite_model)<jupyter_output>INFO:tensorflow:Assets written to: /tmp/tmpy7_9cm5e/assets<jupyter_text>Predict with TS Lite<jupyter_code>!pip install --extra-index-url https://google-coral.github.io/py-repo/ tflite_runtime
import tensorflow.lite as tflite
import tflite_runtime.interpreter as tflite

import os
import numpy as np

from io import BytesIO
from urllib import request

from PIL import Image


MODEL_NAME = MODEL_TFLITE_PATH


def download_image(url):
    with request.urlopen(url) as resp:
        buffer = resp.read()
    stream = BytesIO(buffer)
    img = Image.open(stream)
    return img


def prepare_image(img, target_size):
    if img.mode != 'RGB':
        img = img.convert('RGB')
    img = img.resize(target_size, Image.NEAREST)
    return img


def prepare_input(x):
    return x / 255.0


interpreter = tflite.Interpreter(model_path=MODEL_NAME)
interpreter.allocate_tensors()

input_index = interpreter.get_input_details()[0]['index']
output_index = interpreter.get_output_details()[0]['index']


def predict(url):
    img = download_image(url)
    img = prepare_image(img, target_size=(32, 32))

    x = np.array(img, dtype='float32')
    X = np.array([x])
    X = prepare_input(X)

    interpreter.set_tensor(input_index, X)
    interpreter.invoke()

    preds = interpreter.get_tensor(output_index)

    return preds
url = 'http://clipart-library.com/images/gieERjykT.jpg'
preds = predict(url)
dict(zip(classes, pred[0]))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14184636118598382, 'fraction_numerical': 0.03397349505840072, 'mean_word_length': 4.1057912844036695, 'pattern_counts': {'":': 0, '<': 97, '<?xml version=': 0, '>': 97, 'https://': 6, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 21, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '26', 'dir': 'jupyter-structured-clean-dedup', 'id': '168388', 'license': 'MIT', 'n_tokens_mistral': 6630, 'n_tokens_neox': 6106, 'n_words': 1249, 'path': 'notebook.ipynb', 'repo_name': 'ceccode/traffic-sign-classification', 'size': '17808'}