Criando uma inteligência artificial com Pokemon

import torch
import torch.nn as nn
from torch import optim
import torch.nn.functional as F


from torch.utils.data import DataLoader
from torchvision.utils import make_grid
from torchvision import datasets
import torchvision.transforms as transforms


import numpy as np
import matplotlib.pyplot as plt

import pandas as pd
import opendatasets as od

from torchvision.utils import make_grid

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder

from sklearn.metrics import confusion_matrix, accuracy_score     
from sklearn import preprocessing

import plotly.express as px
import plotly.graph_objects as go
from PIL import Image
import matplotlib.pyplot as plt

import seaborn as sns

od.download('https://www.kaggle.com/datasets/vishalsubbiah/pokemon-images-and-types')
torch.manual_seed(123)
transform = transforms.ToTensor()
le = preprocessing.LabelEncoder()
tipos = pd.read_csv('/content/pokemon-images-and-types/pokemon.csv')

Além disso, estou utilizando preprocessing.LabelEncoder() do Scikit-Learn para converter as categorias dos tipos de Pokémon em números. Isso é útil para que o modelo possa lidar com os dados categóricos de forma numérica durante o treinamento. Para a nossa infelicidade, ele não entende que um tipo fada é um tipo fada, ele vai ter que considerar um tipo fada como um valor em número.

rotulos = {}
for c in range(0, len(tipos)):
  rotulos[list(tipos.Name)[c]] = list(tipos.Type1)[c]
rotulos = dict(sorted(rotulos.items()))

trans = transforms.Compose([
    transforms.Resize((64, 64)), 
    transforms.ToTensor()])

imagens = datasets.ImageFolder('/content/pokemon-images-and-types/', transform=trans)

np.unique(list(rotulos.values()))
encoder = LabelEncoder()
rotulos = encoder.fit_transform(list(rotulos.values()))
print(np.unique(rotulos))

a = 0
for n in imagens.samples:
  imagens.samples[a] = (imagens.samples[a][0], rotulos[a])
  a+=1

img_treinamento, img_teste = train_test_split(imagens, test_size=0.10, shuffle=True)
train_loader = torch.utils.data.DataLoader(img_treinamento, batch_size=126, shuffle=True)
img_testes = torch.utils.data.DataLoader(img_teste)

Inicialmente, a é inicializado com 0 para percorrer os exemplos de imagens e rótulos. Utilizo um loop for para iterar sobre imagens.samples, que contém pares (caminho_da_imagem, índice_do_tipo). Eu acho que foi um jeito muito ruim de fazer isso e sinceramente, se alguém tiver um jeito melhor, por favor, diga aqui nós comentarios. E no final do codigo já separei as imagens que se tornariam parte do treinamento e alguns para a gente fazer a avaliação depois.

class classificador(nn.Module):
  def __init__(self):
    super().__init__()

    self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=(3,3))
    self.conv2 = nn.Conv2d(32, 32, (3, 3))
    self.activation = nn.ReLU()
    self.bnorm = nn.BatchNorm2d(num_features=32)
    self.pool = nn.MaxPool2d(kernel_size = (2,2))
    self.flatten = nn.Flatten()

    self.linear1 = nn.Linear(in_features=6272, out_features=126)
    self.linear2 = nn.Linear(126, 126)
    self.output = nn.Linear(126, 18)
    self.dropout = nn.Dropout(p = 0.2)

  def forward(self, X):
    X = self.pool(self.bnorm(self.activation(self.conv1(X))))
    X = self.pool(self.bnorm(self.activation(self.conv2(X))))
    X = self.flatten(X)

    X = self.dropout(self.activation(self.linear1(X)))
    X = self.dropout(self.activation(self.linear2(X)))
    X = self.output(X)

    return X

Mas resumidamente; arquitetura é projetada para aprender automaticamente características relevantes das imagens dos Pokémon através das camadas convolucionais e classificar corretamente os tipos utilizando as camadas totalmente conectadas. Falando assim fica bem mais fácil de compreender (A forma mais complexa é através de cálculo).

net = classificador()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters())
def training_loop(loader, epoch):
    running_loss = 0.
    running_accuracy = 0.

    for i, data in enumerate(loader):
        inputs, labels = data
        inputs, labels = inputs, labels

        optimizer.zero_grad()
        outputs = net(inputs)

        loss = criterion(outputs, labels)
        loss.backward()

        optimizer.step()

        running_loss += loss.item()

        ps = F.softmax(outputs)
        top_p, top_class = ps.topk(k = 1, dim = 1)
        equals = top_class == labels.view(*top_class.shape)

        accuracy = torch.mean(equals.type(torch.float))

        running_accuracy += accuracy

        print('\rÉpoca {:3d} - Loop {:3d} de {:3d}: perda {:03.2f} - precisão {:03.2f}'.format(epoch + 1, i + 1, len(loader), loss,
                                   accuracy), end = '\r')

    print('\rÉPOCA {:3d} FINALIZADA: perda {:.5f} - precisão {:.5f}'.format(epoch+1, running_loss/len(loader),
                     running_accuracy/len(loader)))
for epoch in range(50):
  print('Treinando...')
  training_loop(train_loader, epoch)
  net.eval()
  print('Validando...')
  training_loop(img_testes, epoch)
  net.train()
matriz = confusion_matrix(resultados_IA, resultados_reais)

fig = go.Figure(data=go.Heatmap(
    z=matriz,
    colorscale='hot',
    showscale=True,
    text=[[str(val) for val in row] for row in matriz], 
    texttemplate="%{text}",
    textfont={"size": 12}
))
fig.update_layout(
    font=dict(family='Courier New, monospace',  size=14, color='white'),
    plot_bgcolor='#070A0D', paper_bgcolor='#070A0D')

fig.show()
precisao = accuracy_score(resultados_reais, resultados_IA)

~ ɢɪᴛʜᴜʙ ᴅᴏ ᴘʀᴏᴊᴇᴛᴏ https://github.com/Borboleta-Vermelha/Pokemons