{"id":14275,"url":"\/distributions\/14275\/click?bit=1&hash=bccbaeb320d3784aa2d1badbee38ca8d11406e8938daaca7e74be177682eb28b","title":"\u041d\u0430 \u0447\u0451\u043c \u0437\u0430\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442 \u043f\u0440\u043e\u0444\u0435\u0441\u0441\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u043e\u0434\u0430\u0432\u0446\u044b \u0430\u0432\u0442\u043e?","buttonText":"\u0423\u0437\u043d\u0430\u0442\u044c","imageUuid":"f72066c6-8459-501b-aea6-770cd3ac60a6"}

Классификация изображений с помощью Machine Learning

Как быстро вы сможете проанализировать изображения? А если их несколько десятков тысяч? Оптимизировать трудозатраты нам поможет Machine Learning. В этой статье делимся опытом классификации изображений.

В работе каждого аудитора возникает необходимость анализа неструктурированных данных, таких как: сканы, фотографии или print screen. Ручная обработка такой информации в больших объемах занимает огромное количество времени. Одним из способов оптимизации трудозатрат является классификация изображений с применением методов Machine Learning. Сегодня хотим поделиться опытом классификации на примере авансовых отчетов.

В нашем случае: мы имели более 30 тыс. различных авансовых отчетов. И задача стояла следующая: проанализировать только те, которые относятся к использованию общественного транспорта, поэтому из общего массива документов необходимо было выделить только билеты на проезд в общественном транспорте.

Здесь нам приходит на помощь ML, а именно — бинарная классификация изображений. Для начала импортируем необходимые модули.

import zipfile import os import random from keras.models import Sequential from keras.layers import Convolution2D from keras.layers import MaxPooling2D from keras.layers import Flatten from keras.layers import Dense from keras.preprocessing.image import ImageDataGenerator from sklearn.metrics import classification_report

Предварительно мы распределили 6 607 изображений на два класса: «билет» и «прочие». Вес архива с изображениями составил 18,5 Гб.

Распаковываем выборку в рабочую директорию.

with zipfile.ZipFile('/opt/workspace/reports/new_data_for_ML.zip', 'r') as zip_ref: zip_ref.extractall('/opt/workspace/reports/')

Считываем пути к выборке.

other = os.listdir('/opt/workspace/reports/other/') tarif = os.listdir('/opt/workspace/reports/tarif/')

Разделяем выборку на обучающую – «train» и тестовую – «test».

random.shuffle(other) random.shuffle(tarif) train_other = other[:round(len(other) * 0.8)] test_other = other[round(len(other) * 0.8):] train_tarif = tarif[:round(len(tarif) * 0.8)] test_tarif = tarif[round(len(tarif) * 0.8):] print('Прочие документы (other): train -', len(train_other), ', test -', len(test_other), ';') print('Билеты (tarif): train -', len(train_tarif), ', test -', len(test_tarif), '.') Прочие документы (other): train - 4894 , test - 1223 ; Билеты (tarif): train - 392 , test - 98 .

Раскладываем изображения в папки для обучения (train) и валидации (test).

for file in other: if file in train_other: os.rename('/opt/workspace/reports/other/' + file, '/opt/workspace/reports/training_set/other/' + file) for file in other: if file in test_other: os.rename('/opt/workspace/reports/other/' + file, '/opt/workspace/reports/test_set/other/' + file) for file in tarif: if file in train_tarif: os.rename('/opt/workspace/reports/tarif/' + file, '/opt/workspace/reports/training_set/tarif/' + file) for file in tarif: if file in test_tarif: os.rename('/opt/workspace/reports/tarif/' + file, '/opt/workspace/reports/test_set/tarif/' + file)

Подготавливаем и настраиваем нашу модель для обучения.

classifier = Sequential() classifier.add(Convolution2D(32, 3, 3, input_shape = (64, 64, 3), activation = 'relu')) classifier.add(MaxPooling2D(pool_size = (2, 2))) classifier.add(Convolution2D(32, 3, 3, activation = 'relu')) classifier.add(MaxPooling2D(pool_size = (2, 2))) classifier.add(Flatten()) classifier.add(Dense(output_dim = 128, activation = 'relu')) classifier.add(Dense(output_dim = 1, activation = 'sigmoid')) classifier.compile(optimizer='adam', loss = 'binary_crossentropy', metrics=['accuracy']) train_datagen = ImageDataGenerator( rescale = 1./255, shear_range = 0.2, zoom_range = 0.2, horizontal_flip = True) test_datagen = ImageDataGenerator(rescale = 1./255)

Определяем обучающую выборку

training_set = train_datagen.flow_from_directory( '/opt/workspace/reports/training_set', target_size=(64, 64), batch_size=32, class_mode='binary') Found 5286 images belonging to 2 classes.

Определяем тестовую выборку

est_set = test_datagen.flow_from_directory( '/opt/workspace/reports/test_set', target_size=(64, 64), batch_size=32, class_mode='binary') Found 1321 images belonging to 2 classes.

Запускаем обучение модели

%%time classifier.fit_generator( training_set, steps_per_epoch=456, epochs=5, validation_data=test_set, validation_steps=114) Epoch 1/5 456/456 [==============================] - 2955s - loss: 0.2356 - acc: 0.9275 - val_loss: 0.1528 - val_acc: 0.9481 Epoch 2/5 456/456 [==============================] - 2862s - loss: 0.1464 - acc: 0.9493 - val_loss: 0.1099 - val_acc: 0.9760 Epoch 3/5 456/456 [==============================] - 2841s - loss: 0.1040 - acc: 0.9659 - val_loss: 0.1002 - val_acc: 0.9631 Epoch 4/5 456/456 [==============================] - 2859s - loss: 0.0805 - acc: 0.9730 - val_loss: 0.0748 - val_acc: 0.9767 Epoch 5/5 456/456 [==============================] - 2847s - loss: 0.0601 - acc: 0.9812 - val_loss: 0.1203 - val_acc: 0.9584 CPU times: user 4h 10min 21s, sys: 9min 16s, total: 4h 19min 38s Wall time: 3h 59min 25s

Наша модель обучилась за 4 часа.

Определяем функцию считывания изображения для предсказания

def load_image(img_path, show=True): img_original = image.load_img(img_path) img = image.load_img(img_path,target_size=(64,64)) img_tensor = image.img_to_array(img) img_tensor = np.expand_dims(img_tensor,axis=0) img_tensor /= 255. return img_tensor

Определяем функцию предсказания класса где 0 это «прочие», а 1 – «билет».

def predict(img_file): new_image = load_image(img_file) pred = classifier.predict(new_image) if pred<.5 : return 0 else: return 1

Подготавливаем датасет для предсказания и оценки точности предсказания нашей модели.

other = os.listdir('/opt/workspace/reports/test_set/other/') tarif = os.listdir('/opt/workspace/reports/test_set/tarif/') df_othet = pd.DataFrame(other) df_othet['target'] = 0 df_othet['path'] = '/opt/workspace/reports/test_set/other/' df_tarif = pd.DataFrame(tarif) df_tarif['target'] = 1 df_tarif['path'] = '/opt/workspace/reports/test_set/tarif/' df = pd.concat([df_othet, df_tarif]).reset_index() df['path'] = df['path'] + df[0] df['predict'] = 0

Проводим предсказание на тестовой выборке и рассчитываем метрики модели.

for ix in df.index: df['predict'][ix] = predict(df['path'][ix]) print(classification_report(df['target'], df['predict'])) precision recall f1-score support 0 0.99 0.97 0.98 1223 1 0.75 0.94 0.83 98 avg / total 0.98 0.97 0.97 1321

Таким образом, с помощью Machine Learning нам удалось выделить 6,6 тыс. билетов на проезд в общественном транспорте, необходимых для анализа, из более чем 30 тыс. авансовых отчетов. Это позволило существенно сократить трудозатраты и исключить просмотр излишней информации, которая составила более 78% общего объема документов.

0
7 комментариев
Написать комментарий...
Артем Симонов

Можно ссылку на исходники?

Ответить
Развернуть ветку
NTA
Автор

Артем, к сожалению, исходники в общий доступ выложить не может. Это внутренняя конфиденциальная информация.

Ответить
Развернуть ветку
Артем Симонов

Понятно, спасибо. К чему тогда вообще было выкладывать не рабочий код не понятно.

Ответить
Развернуть ветку
NTA
Автор

Проверяли, где-то ошибка? Подскажите, на каком шаге возникли трудности?

Ответить
Развернуть ветку
Артем Симонов

Да пытаюсь уже пол дня заставить код работать и получить хоть какой то результат для своих данных, везде представлены кейсы с готовыми наборами данных кошечки собачки, у вас более подходящий кейс к моей задаче

Лезет вот такая ошибка
TypeError: __init__() missing 1 required positional argument: 'units'
Ругается на эту строчку
classifier.add(Dense(output_dim = 128, activation = 'relu'))

Код взял до части

Определяем функцию считывания изображения для предсказания

дальше все равно не двигается, я в питоне не силен где то возможно что то не так скопировал, иерархию папок и прочего повторял по коду

Ответить
Развернуть ветку
NTA
Автор

Артем, добрый день!
1)  одна из возможных причин, почему ошибка может возникать - версия используемой библиотеки. Вот тут похожее описание - https://github.com/mirumee/saleor/issues/4350
2) и еще один вариант решения - https://stackoverflow.com/questions/63840525/typeerror-init-missing-1-required-positional-argument-units

Ответить
Развернуть ветку
Артем Симонов

Доброго времени спасибо, за потраченное время на поиск вариантов решений, тоже нашел данное решение связанное с разницей в версии, к сожалению так и не смог довести код под свою задачу до логического завершения, низкий уровень знаний в данном направлении нужно прокачивать скил

Ответить
Развернуть ветку
4 комментария
Раскрывать всегда