
Compartilhar:
Desenvolvedor iOS que se tornou entusiasta de Ciência de Dados e Aprendizado de Máquina. Quero que as pessoas entendam o que é o aprendizado de máquina e como podemos usá-lo em nossas Applications.
Criação de um classificador de imagens no TensorFlow
Tempo de leitura: 9 minutos
Nesta postagem, você vai criar um modelo básico de classificação de imagens para processar imagens enviadas pelos participantes de uma conversa em um aplicativo para iOS integrado ao Nexmo In-App Messaging. Depois que um usuário enviar uma imagem, será exibida uma legenda descrevendo a imagem.
Vamos usar Python para construir nosso modelo de classificação de imagens. Não se preocupe se você nunca trabalhou com Python ou não tem nenhum conhecimento prévio sobre aprendizado de máquina.
O que é classificação de imagens?
A classificação de imagens no aprendizado de máquina ocorre quando você tem uma foto e o modelo de aprendizado de máquina é capaz de identificar qual objeto está na foto. Por exemplo, se você tirar uma foto de um cachorro, o modelo de aprendizado de máquina será capaz de dizer “Este é um cachorro”.
Primeiro, para construir um modelo de aprendizado de máquina, precisamos ter dados para treiná-lo.
Um modelo de aprendizado de máquina utiliza dados de treinamento para que o modelo aprenda. Para começar, precisaremos escolher os dados de treinamento. Para este post, usaremos o conjunto de dados CIFAIR-10 .
Este conjunto de dados contém imagens em 10 classes, com 6.000 imagens por classe. É um conjunto de dados bastante utilizado em aprendizado de máquina e será um bom ponto de partida para o nosso projeto. Como o conjunto de dados é relativamente pequeno, podemos treinar o modelo rapidamente.
Executando este Notebook
Este notebook está hospedado no Google Colab. O Colaboratory é um ambiente gratuito de notebooks Jupyter que não requer configuração e é executado inteiramente na nuvem.
Observe que você precisará ter um Account do Google para executar o notebook.
É muito fácil executar o notebook. Em cada célula que contém código, há um botão de execução à esquerda da célula. Toque no botão de execução para executar o código. Você também pode usar o comando de teclado Shift e, em seguida, Enter.
Desenvolvendo o modelo
A primeira coisa que precisamos fazer é importar nossos pacotes. Esses pacotes já vêm pré-instalados no Google Colab, portanto, não precisamos instalá-los.
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
import numpy as npObserve que estamos usando o TensorFlow e o Keras como interface para o TensorFlow. O Keras é uma excelente estrutura que permite criar modelos com mais facilidade, sem precisar usar os métodos mais complexos do TensorFlow.
A seguir, vamos carregar o conjunto de dados CIFAR. Com o Keras, podemos baixar o conjunto de dados com muita facilidade.
Dividimos o conjunto de dados em dois grupos: um para treinamento (x_train, y_train)e outro para teste (x_test, y_test).
A divisão do conjunto de dados permite que o modelo aprenda com o conjunto de treinamento. Em seguida, quando testamos o modelo, queremos verificar o quanto ele aprendeu, utilizando o conjunto de teste. Isso nos dará a precisão — ou seja, o desempenho do modelo.
from keras.datasets import cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print('y_train shape', y_train.shape)
print(x_test.shape[0], 'test samples')
print('x_test shape', x_test.shape)
print(y_test.shape[0], 'test samples')Using TensorFlow backend.
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170500096/170498071 [==============================] - 75s 0us/step
x_train shape: (50000, 32, 32, 3)
50000 train samples
y_train shape (50000, 1)
10000 test samples
x_test shape (10000, 32, 32, 3)
10000 test samplesA seguir, vamos declarar algumas constantes.
batch_sizeé o número de amostras que serão propagadas pela rede.epochsé o número de vezes que treinamos com o conjunto de dados completo.class_namesé uma lista de todos os rótulos possíveis no conjunto de dados CIFAR-10.
batch_size = 32
epochs = 100
class_names = ["airplane","automobile","bird","cat","deer","dog","frog","horse","ship","truck"]Usaremos essas constantes mais tarde, ao converter nosso modelo para o CoreML.
def plot_images(x, y, number_of_images=2):
fig, axes1 = plt.subplots(number_of_images,number_of_images,figsize=(10,10))
for j in range(number_of_images):
for k in range(number_of_images):
i = np.random.choice(range(len(x)))
title = class_names[y[i:i+1][0][0]]
axes1[j][k].title.set_text(title)
axes1[j][k].set_axis_off()
axes1[j][k].imshow(x[i:i+1][0])Primeiro, vamos dar uma olhada em algumas imagens. Temos uma função que exibe 4 imagens aleatórias e suas respectivas legendas.
plot_images(x_train, y_train)
Image recognition Tensorflow CoreML
Construindo o modelo
Agora, vamos configurar um modelo simples. Estamos criando uma rede neural profunda usando convoluções, dropoute max pooling.
No final, vamos simplificar a rede e usar Relu, seguido por um Softmax.
Isso nos dará um vetor (matriz unidimensional), composto principalmente por zeros.
Ficará assim.
[0,0,0,0,0,0,1,0,0,0]Esse vetor corresponde ao rótulo fornecido na imagem. Portanto, neste exemplo, o 1 na sétima posição seria um sapo, já que “sapo” está na sétima posição da class_names lista.
A seguir, é apresentada a rede completa.
model = tf.keras.Sequential()
model.add(tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(tf.keras.layers.Conv2D(64, kernel_size=(3, 3), activation='relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Dropout(0.25))
model.add(tf.keras.layers.Conv2D(128, kernel_size=(3, 3), activation='relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Conv2D(128, kernel_size=(3, 3), activation='relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Dropout(0.25))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(1024, activation='relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(10, activation='softmax'))É isso aí!
Treinamento do modelo
Primeiro, compilamos o modelo para obter sua perda. A perda é uma medida do desempenho do modelo durante o teste. Uma perda alta significa que o modelo teve um desempenho ruim.
Aqui você usa o Adam Optimizer, um algoritmo que se estende ao método de descida de gradiente estocástico, amplamente utilizado em aprendizado de máquina, para calcular a perda.
Em seguida, chamaremos .fit , o que treinará o modelo por 100 épocas. Isso significa que o conjunto de dados de treinamento completo será processado 100 vezes. O batch_size valor 32 é o número de amostras que serão propagadas pela rede.
Em seguida, verificamos o desempenho do modelo após cada época usando model.evaluate. Isso nos fornece uma pontuação para o modelo (números mais altos são melhores) e a perda (números mais baixos são melhores).
Observe que isso levou cerca de 15 minutos para ser executado no Colab. Se você quiser ver os resultados mais rapidamente, defina o epochs parâmetro para 1 ou 2. No entanto, a precisão não será tão boa.
# Compile the model
model.compile(loss='categorical_crossentropy',
optimizer=tf.keras.optimizers.Adam(lr=0.0001, decay=1e-6),
metrics=['accuracy'])
# Train the model
model.fit(x_train / 255.0, tf.keras.utils.to_categorical(y_train),
batch_size=batch_size,
shuffle=True,
epochs=epochs,
validation_data=(x_test / 255.0, tf.keras.utils.to_categorical(y_test))
)
# Evaluate the model
scores = model.evaluate(x_test / 255.0, tf.keras.utils.to_categorical(y_test))
print('Loss: %.3f' % scores[0])
print('Accuracy: %.3f' % scores[1])Train on 50000 samples, validate on 10000 samples
Epoch 1/100
#Omitted for readability
50000/50000 [==============================] - 30s 603us/step - loss: 0.1378 - acc: 0.9518 - val_loss: 0.7136 - val_acc: 0.8116
10000/10000 [==============================] - 2s 151us/step
Loss: 0.714
Accuracy: 0.812Nossa precisão final foi de 81%, e nossa perda foi de 0,7, o que é bastante bom.
Para reiterar, a precisão é o grau de eficácia com que o modelo conseguiu classificar cada imagem, enquanto a perda indica o quão ruins foram as previsões do modelo.
Para mais informações, confira esta definição de perda e precisão no curso intensivo de Aprendizado de Máquina do Google.
Conversão do modelo para o Core ML
Depois de treinar o modelo, podemos salvá-lo e, em seguida, convertê-lo para o formato Core ML.
Lançado na WWDC 2018, o Core ML permite que os desenvolvedores de iOS integrem uma ampla variedade de tipos de modelos de aprendizado de máquina em um aplicativo para iOS. Aqui, você utiliza essa tecnologia com o Nexmo In-App Messaging para facilitar seu próprio aprendizado profundo no processamento de imagens.
Primeiro, precisamos salvar o modelo treinado.
model.save('cifar-model.h5')Vamos usar o coremltools, que converterá o modelo para um formato que nosso aplicativo Stitch possa utilizar.
Observe que o pacote Core ML não vem pré-instalado no Colab; portanto, precisamos instalá-lo usando pip
Pelo que se vê acima, dá para perceber que o pacote foi instalado no nosso notebook.
A seguir, vamos converter o modelo salvo para o Core ML.
Como usamos o Keras para treinar nosso modelo, é muito fácil convertê-lo para o Core ML. No entanto, isso varia de acordo com a forma como você construiu seu modelo. As ferramentas do Core ML oferecem outras funções que podem ser utilizadas com outros pacotes de aprendizado de máquina, incluindo o TensorFlow e o Scikit-Learn. Consulte o repositório coremltools para mais informações.
from keras.models import load_model
import coremltools
model = load_model('cifar-model.h5')
coreml_model = coremltools.converters.keras.convert(model,
input_names="image",
image_input_names="image",
image_scale=1/255.0,
class_labels=class_names)
coreml_model.save('CIFAR.mlmodel') 0 : conv2d_input, <keras.engine.topology.InputLayer object at 0x7fa7c829fac8>
1 : conv2d, <keras.layers.convolutional.Conv2D object at 0x7fa7c829f358>
2 : conv2d__activation__, <keras.layers.core.Activation object at 0x7fa7c75bf198>
3 : conv2d_1, <keras.layers.convolutional.Conv2D object at 0x7fa7c80e40b8>
4 : conv2d_1__activation__, <keras.layers.core.Activation object at 0x7fa7c75bf438>
5 : max_pooling2d, <keras.layers.pooling.MaxPooling2D object at 0x7fa7c80e4550>
6 : conv2d_2, <keras.layers.convolutional.Conv2D object at 0x7fa7c77434a8>
7 : conv2d_2__activation__, <keras.layers.core.Activation object at 0x7fa7c73f9240>
8 : max_pooling2d_1, <keras.layers.pooling.MaxPooling2D object at 0x7fa7c7743f28>
9 : conv2d_3, <keras.layers.convolutional.Conv2D object at 0x7fa7c87ad1d0>
10 : conv2d_3__activation__, <keras.layers.core.Activation object at 0x7fa7c7262dd8>
11 : max_pooling2d_2, <keras.layers.pooling.MaxPooling2D object at 0x7fa7c7743f60>
12 : flatten, <keras.layers.core.Flatten object at 0x7fa7c76fac50>
13 : dense, <keras.layers.core.Dense object at 0x7fa7c76b42b0>
14 : dense__activation__, <keras.layers.core.Activation object at 0x7fa7c71ff390>
15 : dense_1, <keras.layers.core.Dense object at 0x7fa7c7670358>
16 : dense_1__activation__, <keras.layers.core.Activation object at 0x7fa7c71ff898>A saída acima mostra todas as camadas dentro do modelo. Elas se relacionam diretamente com a forma como criamos o modelo neste célula.
Dê uma olhada nos parâmetros da convert função. Aqui, vamos definir a entrada para que seja um image para ambos o input_names e image_input_names . Isso ajudará o modelo do Core ML a saber que tipo de entrada ele está esperando, que é uma imagem.
Em seguida, reduzimos o tamanho das imagens no image_scale parâmetro para um número entre 0 e 1.
Em seguida, definimos o class_labels parâmetro para class_names constante que criamos anteriormente.
Quando usarmos esse modelo no Xcode, o resultado será um String, correspondente ao rótulo previsto para a imagem.
Agora, podemos dar uma olhada no modelo do Core ML.
print(coreml_model)input {
name: "image"
type {
imageType {
width: 32
height: 32
colorSpace: RGB
}
}
}
output {
name: "output1"
type {
dictionaryType {
stringKeyType {
}
}
}
}
output {
name: "classLabel"
type {
stringType {
}
}
}
predictedFeatureName: "classLabel"
predictedProbabilitiesName: "output1"Você pode ver que nossa input é uma imagem de 32x32 pixels, e nossa saída é uma String, chamada classLabel
Em seguida, salvamos o mlmodel localmente usando um pacote do Google Colab para baixar o arquivo para o nosso computador.
from google.colab import files
files.download('CIFAR.mlmodel') Incorporando o modelo ao nosso aplicativo Stitch
Depois que nosso modelo estiver salvo, já podemos importá-lo para nosso aplicativo. Para isso, basta arrastar o modelo que acabou de ser salvo para o Xcode.
CIFAR xcode
Certifique-se de que o modelo esteja incluído no destino, verificando se a opção “Pertencimento ao destino” está selecionada.
A seguir, vamos escrever o código em nosso aplicativo para iOS que utilizará esse modelo.
Em nosso aplicativo de demonstração do Stitch, os usuários podem enviar uma foto para uma conversa já existente.
O recurso de In-App Messaging da Nexmo permite que os usuários, como participantes de uma conversa, acionem não apenas TextEvents mas ImageEvents ao enviar uma foto para uma conversa já existente. Neste exemplo, tentaremos prever o conteúdo da foto que um usuário enviou.
Você integra a funcionalidade para observar ImageEvents o Core ML diretamente no seu ViewController. Um exemplo de como isso pode ser feito pode ser encontrado no código-fonte deste exemplo.
Em nosso ViewController, vamos instanciar o modelo.
let model = CIFAR()Agora, dentro do cellForRowAtPath método, vamos verificar se o event existe um ImageEvent, e, se for o caso, exibiremos a foto do ImageEvent. Em seguida, pegamos a imagem, convertemos para um PixelBuffer, com tamanho de 32x32 pixels, e a insere no modelo.
A razão pela qual precisamos redimensionar a imagem é que o modelo foi treinado com imagens de 32x32 pixels; portanto, se não redimensionarmos as imagens, o modelo não conseguirá fornecer uma previsão (aparecerá uma mensagem de erro no Xcode informando que o tamanho da imagem está incorreto).
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let event = conversation?.events[indexPath.row]
switch event {
case is ImageEvent:
//get the image from the ImageEvent
let imageEvent = (event as! ImageEvent)
guard let imagePath = imageEvent.path(of: IPS.ImageType.thumbnail), let image = UIImage(contentsOfFile: imagePath) else {
break
}
cell.imageView?.image = image
//convert the image to a pixelBuffer
//using https://github.com/hollance/CoreMLHelpers.git
if let pixelBuffer = image.pixelBuffer(width: 32, height: 32) {
let input = CIFARInput(image: pixelBuffer)
//perform the prediction
if let output = try? model.prediction(input: input) {
cell.textLabel?.text = (imageEvent.from?.name)! + " uploaded a photo of a \(output.classLabel)"
}
else {
cell.textLabel?.text = (imageEvent.from?.name)! + " uploaded a photo"
}
}
break;
default:
cell.textLabel?.text = ""
}
return cell;
}O modelo retornará, então, um classLabel. Esse será o nome da imagem que o modelo previu, que pode ser um dos seguintes rótulos: “avião”, “carro”, “pássaro”, “gato”, “veado”, “cachorro”, “sapo”, “cavalo”, “navio” ou “caminhão”
Conclusão
Depois de analisar nossas previsões, podemos concluir que o modelo será capaz de reconhecer apenas 10 rótulos. O notebook completo está disponível no GitHub.
Isso serve para uma demonstração, mas não para uma aplicação de produção. Em um post futuro, veremos como construir um modelo de reconhecimento de imagens com mais dados. Vamos explorar o popular banco de dados ImageNet, que contém 14.197.122 imagens rotuladas.
É um download de 150 GB, então vamos ver como baixá-lo, treiná-lo e integrá-lo ao nosso aplicativo de demonstração do Stitch.