from fastai2.vision.all import *
Instalar fastai2
http://dev.fast.ai/#Installing yo prefiero instalarlo como dice ahí en su propio entorno de conda.
git clone https://github.com/fastai/fastai2
cd fastai2
pip install -e ".[dev]"
para que CUDA este disponible python -c 'import torch; print(torch.cuda.is_available())'
si no lo esta checa que este instalado con nvidia-smi
, si esta instalado puede que no se haya instalado la versión correcta de pytorch y los drivers instalados ve como hacerlo en https://pytorch.org/get-started/locally/
Registrandose en kaggle
- Registrarse como usuario en kaggle
- hacer
pip install kaggle
- Obtener tu clave privada de usuario y guardarla en la ruta default
- Entrar en https://www.kaggle.com/c/digit-recognizer y dar click en aceptar reglas para poder usar la aplicación de kaggle.
- Bajar los archivos con
kaggle competitions download -c digit-recognizer
y descomprimirlos en su propia carpeta
Importar fastai2
Importamos el modulo vision de fastai2
Vamos a definir algunas variables para poder ponerle nombre a nuestros archivos a enviar y guardar el nombre del modelo que usamos.
= 4
VERSION =resnet34
MODELO="resnet34"
NOMBRE_MODELO= f"base-{NOMBRE_MODELO}_v{VERSION}"
BASE_FILE = f"submit-{NOMBRE_MODELO}_v{VERSION}"
SUBMIT_FILE
= f"base-{NOMBRE_MODELO}-fine_v{VERSION}"
FINE_FILE = f"submit-{NOMBRE_MODELO}-fine_v{VERSION}" SUBMIT_FINE_FILE
Fastai2 trabaja muy bien con data loaders, estos necesitan que exista algun archivo en disco, lo cual puede ayudar para datasets grandes, pero tal vez no mucho a su lectura recurrente. Sólo necesitamos preprocesar esto la primer vez, por lo mismo usamos esos if
s para extraerlos si es necesario desde cada row del csv
hacia la imagen en disco.
import string
# in the same folder I have downloaded with: kaggle competitions download -c digit-recognizer
= untar_data("file://digit-recognizer.zip", dest=".")
path #print(path)
#print(path.ls())
from PIL import Image
from matplotlib import cm
import pandas as pd
= Path("digit-recognizer")
path = pd.read_csv(path/"train.csv", header='infer')
df df.head()
label | pixel0 | pixel1 | pixel2 | pixel3 | pixel4 | pixel5 | pixel6 | pixel7 | pixel8 | ... | pixel774 | pixel775 | pixel776 | pixel777 | pixel778 | pixel779 | pixel780 | pixel781 | pixel782 | pixel783 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
5 rows × 785 columns
%%time
def get_image(csv_row):
= csv_row.reshape(28,28)
img = cm.gist_earth(img)*255
x return Image.fromarray(np.uint8(x))
def explode_train():
= pd.read_csv(path/'train.csv', header='infer')
df = df.iloc[:,1:].apply(lambda x: x.values, axis=1).values
imagenes = df.iloc[:,:1].apply(lambda x: x.values[0], axis=1)
labels = Path(path)/'train'
p =True, exist_ok=True)
p.mkdir(parents
for idx, l in enumerate(labels):
= get_image(imagenes[idx])
im = (str(l)+"_"+'train'+'_'+''.join(random.choice(string.ascii_lowercase) for i in range(8))) + ".png"
image_name /image_name)
im.save(p
def explode_test():
= pd.read_csv(path/'test.csv', header='infer')
df = df.iloc[:,:].apply(lambda x: x.values, axis=1).values
imagenes = Path(path)/'test'
p =True, exist_ok=True)
p.mkdir(parentsfor idx, l in enumerate(imagenes):
= get_image(imagenes[idx])
im / f"{idx}.png")
im.save(p if not (path/'train').exists():
explode_train()if not (path/'test').exists():
explode_test()
CPU times: user 106 µs, sys: 13 µs, total: 119 µs
Wall time: 81.1 µs
Para leer todas las imagenes generadas, lo haciemos por medio de un DataBlock
el cual es un blueprint
de donde, como y que vamos a hacer con esas imagenes para obtener finalmente las imagenes en batches
o grupos de imagenes (de 64 en 64 por default) y la carga de las imagenes como tal se hace por medio de data_loader = data_block.dataloaders(path)
el cual en este caso también aprende el “vocabulario” a aprender ya que se ha especificado que el segundo bloque es un bloque de categorias a diferencia del primero que es de Imagenes.
get_my_labels
obtiene a partir de el archivo de imagen leído el nombre y regresa el dígito al que pertenece del vocabulario.
%%time
def get_my_labels(fname):
return int(fname.name[0])
= DataBlock(
dblock = RandomSplitter(),
splitter = Resize(224),
item_tfms = (ImageBlock, CategoryBlock),
blocks = get_image_files,
get_items = get_my_labels
get_y
)
= dblock.dataloaders("digit-recognizer/train")
dls dls.vocab
CPU times: user 4.12 s, sys: 549 ms, total: 4.67 s
Wall time: 4.81 s
(#10) [0,1,2,3,4,5,6,7,8,9]
show batch
Podemos ver que realmente este encontrando las imagenes mostrando un batch de imagenes.
dls.show_batch()
Cargar un modelo y entrenarlo
En fastai2 podemos cargar un modelo ya entrenado y reusarlo para ajustarlo a la tarea que queremos llevar a cabo.
= cnn_learner(dls, MODELO, metrics=error_rate) learn
Nuestro modelo en este momento ya tiene una estructura la cual se puede sumarizar así
learn.summary()
Sequential (Input shape: ['64 x 3 x 224 x 224'])
================================================================
Layer (type) Output Shape Param # Trainable
================================================================
Conv2d 64 x 64 x 112 x 112 9,408 False
________________________________________________________________
BatchNorm2d 64 x 64 x 112 x 112 128 True
________________________________________________________________
ReLU 64 x 64 x 112 x 112 0 False
________________________________________________________________
MaxPool2d 64 x 64 x 56 x 56 0 False
________________________________________________________________
Conv2d 64 x 64 x 56 x 56 36,864 False
________________________________________________________________
BatchNorm2d 64 x 64 x 56 x 56 128 True
________________________________________________________________
ReLU 64 x 64 x 56 x 56 0 False
________________________________________________________________
Conv2d 64 x 64 x 56 x 56 36,864 False
________________________________________________________________
BatchNorm2d 64 x 64 x 56 x 56 128 True
________________________________________________________________
Conv2d 64 x 64 x 56 x 56 36,864 False
________________________________________________________________
BatchNorm2d 64 x 64 x 56 x 56 128 True
________________________________________________________________
ReLU 64 x 64 x 56 x 56 0 False
________________________________________________________________
Conv2d 64 x 64 x 56 x 56 36,864 False
________________________________________________________________
BatchNorm2d 64 x 64 x 56 x 56 128 True
________________________________________________________________
Conv2d 64 x 64 x 56 x 56 36,864 False
________________________________________________________________
BatchNorm2d 64 x 64 x 56 x 56 128 True
________________________________________________________________
ReLU 64 x 64 x 56 x 56 0 False
________________________________________________________________
Conv2d 64 x 64 x 56 x 56 36,864 False
________________________________________________________________
BatchNorm2d 64 x 64 x 56 x 56 128 True
________________________________________________________________
Conv2d 64 x 128 x 28 x 28 73,728 False
________________________________________________________________
BatchNorm2d 64 x 128 x 28 x 28 256 True
________________________________________________________________
ReLU 64 x 128 x 28 x 28 0 False
________________________________________________________________
Conv2d 64 x 128 x 28 x 28 147,456 False
________________________________________________________________
BatchNorm2d 64 x 128 x 28 x 28 256 True
________________________________________________________________
Conv2d 64 x 128 x 28 x 28 8,192 False
________________________________________________________________
BatchNorm2d 64 x 128 x 28 x 28 256 True
________________________________________________________________
Conv2d 64 x 128 x 28 x 28 147,456 False
________________________________________________________________
BatchNorm2d 64 x 128 x 28 x 28 256 True
________________________________________________________________
ReLU 64 x 128 x 28 x 28 0 False
________________________________________________________________
Conv2d 64 x 128 x 28 x 28 147,456 False
________________________________________________________________
BatchNorm2d 64 x 128 x 28 x 28 256 True
________________________________________________________________
Conv2d 64 x 128 x 28 x 28 147,456 False
________________________________________________________________
BatchNorm2d 64 x 128 x 28 x 28 256 True
________________________________________________________________
ReLU 64 x 128 x 28 x 28 0 False
________________________________________________________________
Conv2d 64 x 128 x 28 x 28 147,456 False
________________________________________________________________
BatchNorm2d 64 x 128 x 28 x 28 256 True
________________________________________________________________
Conv2d 64 x 128 x 28 x 28 147,456 False
________________________________________________________________
BatchNorm2d 64 x 128 x 28 x 28 256 True
________________________________________________________________
ReLU 64 x 128 x 28 x 28 0 False
________________________________________________________________
Conv2d 64 x 128 x 28 x 28 147,456 False
________________________________________________________________
BatchNorm2d 64 x 128 x 28 x 28 256 True
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 294,912 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
ReLU 64 x 256 x 14 x 14 0 False
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 32,768 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
ReLU 64 x 256 x 14 x 14 0 False
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
ReLU 64 x 256 x 14 x 14 0 False
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
ReLU 64 x 256 x 14 x 14 0 False
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
ReLU 64 x 256 x 14 x 14 0 False
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
ReLU 64 x 256 x 14 x 14 0 False
________________________________________________________________
Conv2d 64 x 256 x 14 x 14 589,824 False
________________________________________________________________
BatchNorm2d 64 x 256 x 14 x 14 512 True
________________________________________________________________
Conv2d 64 x 512 x 7 x 7 1,179,648 False
________________________________________________________________
BatchNorm2d 64 x 512 x 7 x 7 1,024 True
________________________________________________________________
ReLU 64 x 512 x 7 x 7 0 False
________________________________________________________________
Conv2d 64 x 512 x 7 x 7 2,359,296 False
________________________________________________________________
BatchNorm2d 64 x 512 x 7 x 7 1,024 True
________________________________________________________________
Conv2d 64 x 512 x 7 x 7 131,072 False
________________________________________________________________
BatchNorm2d 64 x 512 x 7 x 7 1,024 True
________________________________________________________________
Conv2d 64 x 512 x 7 x 7 2,359,296 False
________________________________________________________________
BatchNorm2d 64 x 512 x 7 x 7 1,024 True
________________________________________________________________
ReLU 64 x 512 x 7 x 7 0 False
________________________________________________________________
Conv2d 64 x 512 x 7 x 7 2,359,296 False
________________________________________________________________
BatchNorm2d 64 x 512 x 7 x 7 1,024 True
________________________________________________________________
Conv2d 64 x 512 x 7 x 7 2,359,296 False
________________________________________________________________
BatchNorm2d 64 x 512 x 7 x 7 1,024 True
________________________________________________________________
ReLU 64 x 512 x 7 x 7 0 False
________________________________________________________________
Conv2d 64 x 512 x 7 x 7 2,359,296 False
________________________________________________________________
BatchNorm2d 64 x 512 x 7 x 7 1,024 True
________________________________________________________________
AdaptiveAvgPool2d 64 x 512 x 1 x 1 0 False
________________________________________________________________
AdaptiveMaxPool2d 64 x 512 x 1 x 1 0 False
________________________________________________________________
Flatten 64 x 1024 0 False
________________________________________________________________
BatchNorm1d 64 x 1024 2,048 True
________________________________________________________________
Dropout 64 x 1024 0 False
________________________________________________________________
Linear 64 x 512 524,288 True
________________________________________________________________
ReLU 64 x 512 0 False
________________________________________________________________
BatchNorm1d 64 x 512 1,024 True
________________________________________________________________
Dropout 64 x 512 0 False
________________________________________________________________
Linear 64 x 10 5,120 True
________________________________________________________________
Total params: 21,817,152
Total trainable params: 549,504
Total non-trainable params: 21,267,648
Optimizer used: <function Adam at 0x7fb52353ab00>
Loss function: FlattenedLoss of CrossEntropyLoss()
Model frozen up to parameter group number 2
Callbacks:
- TrainEvalCallback
- Recorder
- ProgressCallback
Podemos ver las predicciones actuales antes de reajustar los parametros de la red. En rojo se muestran los errores en verde los que estan bien. Los aciertos más bien son aleatorios.
Los resultados como se espera no pueden reconocer los patrones ya que esta inicializada para otro tipo de tarea.
learn.show_results()
Ahora podemos entrenarlo con fine_tune
El método fit sirve para ejecutar una serie de pasadas sobre los datos de entrenamiento.
%%time
1) learn.fine_tune(
epoch | train_loss | valid_loss | error_rate | time |
---|---|---|---|---|
0 | 0.227946 | 0.135213 | 0.040000 | 01:16 |
epoch | train_loss | valid_loss | error_rate | time |
---|---|---|---|---|
0 | 0.035625 | 0.025032 | 0.007024 | 01:43 |
CPU times: user 2min 15s, sys: 26.2 s, total: 2min 42s
Wall time: 3min
learn.show_results()
Nuestro modelo en este momento ya puede asertar con más confianza el dígito que se le esta pasando
"digit-recognizer")/'test'/"1111.png") learn.predict(Path(
('2',
tensor(2),
tensor([8.4126e-10, 8.9289e-08, 1.0000e+00, 7.2959e-07, 2.4069e-09, 2.0494e-08,
2.9449e-09, 1.0405e-07, 2.0946e-07, 1.2205e-10]))
Si volvemos a checar los resultados podemos ver que ciertamente ya puede reconocer los patrones.
Veamos cuales son las imagenes que más costaron reconocer.
= Interpretation.from_learner(learn)
interp 9, figsize=(15,10)) interp.plot_top_losses(
Para guardar los resultados obtenidos y mandarlos a kaggle, basta con hacer lo siguiente
def predict_test(the_learner, file_name):
= path/'test'
p print(f"predicting {len(p.ls())} in {p}")
= []
l for idx, img in enumerate(p.ls()):
= p/f"{idx}.png"
fname = the_learner.predict(fname)
pred # la predicción contiene todo el resultado, la predicción esta en el elemento 0
+1, int(pred[0])] )
l.append( [idxif idx % 2800 == 0:
print(f"{[idx, pred[0]]}...")
= pd.DataFrame(l)
df = ["ImageId","label"]
h f"{file_name}.csv", header=h, index=False) if True else print("** skipped save **")
df.to_csv(print("done!")
Removemos el callback del loader
%%time
2])
learn.remove_cb(learn.cbs[ predict_test(learn, BASE_FILE)
predicting 28000 in digit-recognizer/test
[0, '2']...
[2800, '4']...
[5600, '8']...
[8400, '5']...
[11200, '4']...
[14000, '3']...
[16800, '1']...
[19600, '5']...
[22400, '9']...
[25200, '3']...
done!
CPU times: user 53min 39s, sys: 1h 36min 21s, total: 2h 30min
Wall time: 2h 49min 33s
El Learner
que actualmente se encuentra en learn
es muy bueno para quedar cerca de los primeros mil competidores, lo que tenemos que hacer ahora es mejorarlo poco a poco.
Pero antes de esto vamos a salvarlo de 2 formas:
- exportandolo el cual requiere que despues se vuelva a cargar y crear otro cnn
- salvar y cargar de manera directa
Guardamos el modelo actual para poder cargarlo despues via load
.
if True else print("No se a guardado el archivo") learn.save(BASE_FILE)
Fine tunning
Ahora se puede cargar desde donde se quedo el paso guardado por save anterior, pero debe de contener datos a los cuales referirse, por eso se carga a travez de un modelo instanciado igual que antes, sólo que este ya esta “entrenado” a la tarea actual.
= cnn_learner(dls, MODELO, metrics=error_rate) l2
Una vez instanciado igual que el modelo que guardamos, se puede cargar el mismo desde el archivo que se guardo con learn.save
l2.load(BASE_FILE)
<fastai2.learner.Learner at 0x7fb5202f10d0>
Buscamos un buen learning rate
l2.lr_find()
SuggestedLRs(lr_min=0.00043651582673192023, lr_steep=6.309573450380412e-07)
Descongelamos el modelo entrenado para que se pueda entrenar nuevamente y usamos el learning rate
que encontramos anteriormente
%%time
l2.unfreeze()8, 1e-2) l2.fine_tune(
epoch | train_loss | valid_loss | error_rate | time |
---|---|---|---|---|
0 | 0.080212 | 0.057889 | 0.010238 | 01:10 |
epoch | train_loss | valid_loss | error_rate | time |
---|---|---|---|---|
0 | 0.074369 | 0.067521 | 0.013690 | 01:36 |
1 | 0.082539 | 0.081573 | 0.018690 | 01:36 |
2 | 0.058147 | 0.052700 | 0.015238 | 01:35 |
3 | 0.042055 | 0.044290 | 0.009048 | 01:34 |
4 | 0.029418 | 0.040308 | 0.009048 | 01:33 |
5 | 0.008509 | 0.022320 | 0.004286 | 01:34 |
6 | 0.003744 | 0.021935 | 0.004286 | 01:36 |
7 | 0.000918 | 0.023328 | 0.004524 | 01:35 |
CPU times: user 10min 46s, sys: 2min 52s, total: 13min 38s
Wall time: 13min 53s
Guardamos el nuevo modelo ajustado
l2.save(FINE_FILE)
Y lo usamos para predecir eliminando también el callback del progress bar
2] l2.cbs, l2.cbs[
((#3) [TrainEvalCallback,Recorder,ProgressCallback], ProgressCallback)
%%time
2])
l2.remove_cb(l2.cbs[ predict_test(l2, FINE_FILE)
predicting 28000 in digit-recognizer/test
[0, '2']...
[2800, '4']...
[5600, '8']...
[8400, '5']...
[11200, '4']...
[14000, '3']...
[16800, '1']...
[19600, '5']...
[22400, '9']...
[25200, '3']...
done!
CPU times: user 39min 42s, sys: 1h 35min 30s, total: 2h 15min 13s
Wall time: 2h 36min 45s
l2.validate()
(#2) [0.023327725008130074,0.0045238095335662365]
Exportar a producción
Si no queremos seguir reentrenando nuestro trabajo, podemos sólo exportar el modelo para que se use en predicciones al cargarlo.
if False else print("no se ha exportado") learn.export(BASE_FILE)
no se ha exportado
if False else print("no se ha exportado nada") l2.export(FINE_FILE)
no se ha exportado nada