<jupyter_start><jupyter_text>Классификатор Подготовка датасетаДля обучения классификатора будем использовать подборку RTSD-R3, в которой представлены 106 классов различных знаков. Изображения заранее вырезаны и разбиты на две группы - для обучения и для тестирования - в отношении 3 к 1. Всего представлено 94 тысячи изображений. Рассмотрим распределение сборок по классам:<jupyter_code>def get_classes_counts(*, filename, new_filename, label):
    df = pd.read_csv(filename)
    cr = cs.Counter(df[label].values)
    new_df = pd.DataFrame.from_dict(cr, orient='index').reset_index()
    new_df.columns = (label, 'count')
    new_df.to_csv(new_filename, index=False)


get_classes_counts(filename='dataset/gt_test.csv',
                   new_filename='dataset/gt_test_count.csv',
                   label='class_number')
get_classes_counts(filename='dataset/gt_train.csv',
                   new_filename='dataset/gt_train_count.csv',
                   label='class_number')
plt.figure(figsize=(12,8))
first = pd.read_csv('dataset/gt_train_count.csv')
plt.bar(first['class_number'], height=first['count'])
second = pd.read_csv('dataset/gt_test_count.csv')
plt.bar(second['class_number'], height=second['count'])
# plt.yscale('log')
plt.legend(['Train', 'Test'], loc='upper left')
plt.xlabel('Class number')
plt.ylabel('Count')
plt.title('Signs count in dataset')
plt.savefig('results/report/test_train_dist.png')<jupyter_output><empty_output><jupyter_text>Из приведенного выше графика видно, что данные не распределены равномерно. Более того, некоторые класы вообще не присутствуют в тестовой выборке.Датасет придется снова собрать и уже затем случайно разбивать на выборки в заданном соотношении.Изспользуем 2.25 : 0.75 : 1<jupyter_code>def merge_csvs_and_dirs(*, files, paths_from,
                        path_to, new_filename,
                        name_label='filename',
                        pic_format='.png'):
    offset = 0
    data = []
    for file, path in zip(files, paths_from):
        df = pd.read_csv(file)
        for index, row in df.iterrows():
            new_name = str(offset + index).zfill(6) + pic_format
            sh.copyfile(path + row[name_label], path_to + new_name)
            row[name_label] = new_name
            data.append(row)
        offset = len(data)
    pd.DataFrame(data).to_csv(new_filename,  index=False)


merge_csvs_and_dirs(files=['dataset/gt_train.csv', 'dataset/gt_test.csv'],
                    paths_from=['dataset/train/', 'dataset/test/'],
                    path_to='dataset/dataset/',
                    new_filename='dataset/dataset.csv')
!rm dataset/gt_train.csv
!rm dataset/gt_test.csv
!rm -r dataset/train
!rm -r dataset/test
!mkdir dataset/train
!mkdir dataset/test
!mkdir dataset/validation
from sklearn.model_selection import train_test_split

def split_dataframe(*, filename, f_new_filename,
                    s_new_filename, size_ratio,
                    from_label):
    df = pd.read_csv(filename)
    x = df.class_number
    y = df.drop(from_label, axis=1)
    x_train, x_test, y_train, y_test = train_test_split(
        x, y, test_size=size_ratio, random_state=42
    )
    train = pd.concat([y_train, x_train], axis=1)
    train.to_csv(f_new_filename, index=False)
    test = pd.concat([y_test, x_test], axis=1)
    test.to_csv(s_new_filename, index=False)


def move_with_csv(*, filename, path_from, path_to,
                  name_label = 'filename',
                  pic_format = '.png'):
    df = pd.read_csv(filename)
    new_data = []
    for index, row in df.iterrows():
        new_name = str(index).zfill(6) + pic_format
        sh.copyfile(path_from + row[name_label], path_to + new_name)
        row[name_label] = new_name
        new_data.append(row)
    pd.DataFrame(new_data, columns=list(df.columns)).to_csv(filename, index=False)


split_dataframe(filename='dataset/dataset.csv',
                f_new_filename='dataset/train.csv',
                s_new_filename='dataset/test.csv',
                size_ratio=0.25,
                from_label='class_number')
split_dataframe(filename='dataset/train.csv',
                f_new_filename='dataset/train.csv',
                s_new_filename='dataset/validation.csv',
                size_ratio=0.25,
                from_label='class_number')
move_with_csv(filename = 'dataset/train.csv',
              path_from = 'dataset/dataset/',
              path_to = 'dataset/train/')
move_with_csv(filename = 'dataset/test.csv',
              path_from = 'dataset/dataset/',
              path_to = 'dataset/test/')
move_with_csv(filename = 'dataset/validation.csv',
              path_from = 'dataset/dataset/',
              path_to = 'dataset/validation/')<jupyter_output><empty_output><jupyter_text>Так же для уравнивания весов классов (так как датасет очень неоднороден), понадобится csv-файл с распределением по классам.<jupyter_code>get_classes_counts(filename='dataset/train.csv',
                   new_filename='dataset/classes_count.csv',
                   label='class_number')<jupyter_output><empty_output><jupyter_text>Подготовка датасета завершена. Получили три необходимых набора: для обучения, для валидации и для тестирования.  Распределение bounding box'ов по положению на изображениях<jupyter_code>df = pd.read_csv('full-gt.csv')
is_tiny = (df['height'] < 31) & (df['width'] < 31)
tiny = df[is_tiny]
tiny.to_csv('tiny.csv', index=False)
is_large = (df['height'] > 30) & (df['width'] > 30)
large = df[is_large]
large.to_csv('large.csv', index=False)
import matplotlib.patches as ptc
from PIL import Image

def create_rectangles(x_col, y_col, width, height):
    rectangles = []
    for x, y, w, h in zip(x_col, y_col, width, height):
        rectangles.append(ptc.Rectangle((x,y),w,h,linewidth=1,edgecolor='r',facecolor='none'))
    return rectangles

df = pd.read_csv('large.csv')
rects = create_rectangles(df['x_from'], df['y_from'], df['width'], df['height'])
fig, ax = plt.subplots(1, figsize=(15, 15))
ax.imshow(Image.new('RGB', (1920, 1080), color = (0, 0, 0)))
for i in rects:
    ax.add_patch(i)
fig.savefig('results/report/large.png')<jupyter_output><empty_output><jupyter_text>Распределение bbox'ов по входному изображениюДля изображений меньше 30x30:Для изображений больше 30x30:Исходя из этих "графиков" можно оценить размеры части изображения, в которой не встречаются знаки и которую, например, можно просто не учитывать при подаче изображения в нейронную сеть.// Изменить размер изображений  Обучение и обработка результатовВ результате обучения получаем log-файл, содержащий loss и accuracy, характеризующие модель на каждой эпохе обучения. В моих моделях валидационная сборка прогонялась раз в две эпохи, поэтому метрики на этом этапе в логах не присутствуют.  Визуализация метрик во время процесса обучения<jupyter_code>filename = 'results/log.csv'

df = pd.read_csv(filename)
ep = list(range(df.shape[0]))
fig, axs = plt.subplots(nrows=2, figsize=(12,16))
fig.suptitle('Model report', size=22)
axs[0].plot(df['acc'])
axs[0].set_title('Model accuracy', size=16)
axs[0].grid(linewidth=1, linestyle='--')
axs[0].set_ylabel('Loss')
axs[0].set_xlabel('Epoch')
axs[0].set_xticks(np.arange(0, 500, step=50))
axs[0].set_yticks(np.arange(0.5, 1, step=0.025))
axs[1].plot(df['loss'])
axs[1].set_title('Model loss', size=16)
axs[1].grid(linewidth=1, linestyle='--')
axs[1].set_ylabel('Loss')
axs[1].set_xlabel('Epoch')
axs[1].set_xticks(np.arange(0, 500, step=50))
fig.savefig('results/report/A2_500_train_report.png')<jupyter_output><empty_output><jupyter_text>Запуск tensroboard<jupyter_code>!tensorboard --logdir=results/tb_logs
def slice_df(*, filename,
                new_filename,
                directory,
                new_directory,
                name_label='filename',
                first=0, second=100):
    df = pd.read_csv(filename)
    df = df.loc[first: second, :]
    for index, row in df.iterrows():
        sh.copyfile(directory + row[name_label], new_directory + row[name_label])
    df.to_csv(new_filename, index=False)

slice_df(filename='dataset/test.csv',
        new_filename='dataset/predict.csv',
        directory='dataset/test/',
        new_directory='dataset/predict/')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12795515476480623, 'fraction_numerical': 0.01450158420667804, 'mean_word_length': 3.7604408352668215, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, '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': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '165366', 'license': 'MIT', 'n_tokens_mistral': 3004, 'n_tokens_neox': 3069, 'n_words': 540, 'path': 'notes.ipynb', 'repo_name': 'bmstu-iu8-g1-2019-project/road_signs_recognition', 'size': '8206'}