{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tiny Imagenet"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"os.environ['CUDA_VISIBLE_DEVICES']='2'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import shutil,timm,os,torch,random,datasets,math,warnings\n",
"import fastcore.all as fc, numpy as np, matplotlib as mpl, matplotlib.pyplot as plt\n",
"import k_diffusion as K, torchvision.transforms as T\n",
"import torchvision.transforms.functional as TF,torch.nn.functional as F\n",
"\n",
"from torch.utils.data import DataLoader,default_collate\n",
"from pathlib import Path\n",
"from torch.nn import init\n",
"from fastcore.foundation import L\n",
"from torch import nn,tensor\n",
"from operator import itemgetter\n",
"from torcheval.metrics import MulticlassAccuracy\n",
"from functools import partial\n",
"from torch.optim import lr_scheduler\n",
"from torch import optim\n",
"from torchvision.io import read_image,ImageReadMode\n",
"from glob import glob\n",
"\n",
"from miniai.datasets import *\n",
"from miniai.conv import *\n",
"from miniai.learner import *\n",
"from miniai.activations import *\n",
"from miniai.init import *\n",
"from miniai.sgd import *\n",
"from miniai.resnet import *\n",
"from miniai.augment import *\n",
"from miniai.accel import *\n",
"from miniai.training import *"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from fastprogress import progress_bar"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"torch.set_printoptions(precision=5, linewidth=140, sci_mode=False)\n",
"torch.manual_seed(1)\n",
"mpl.rcParams['figure.dpi'] = 70\n",
"\n",
"set_seed(42)\n",
"if fc.defaults.cpus>8: fc.defaults.cpus=8"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"path_data = Path('data')\n",
"path_data.mkdir(exist_ok=True)\n",
"path = path_data/'tiny-imagenet-200'\n",
"\n",
"url = 'http://cs231n.stanford.edu/tiny-imagenet-200.zip'\n",
"if not path.exists():\n",
" path_zip = fc.urlsave(url, path_data)\n",
" shutil.unpack_archive('data/tiny-imagenet-200.zip', 'data')\n",
"\n",
"bs = 512\n",
"\n",
"class TinyDS:\n",
" def __init__(self, path):\n",
" self.path = Path(path)\n",
" self.files = glob(str(path/'**/*.JPEG'), recursive=True)\n",
" def __len__(self): return len(self.files)\n",
" def __getitem__(self, i): return self.files[i],Path(self.files[i]).parent.parent.name\n",
"\n",
"tds = TinyDS(path/'train')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"path_anno = path/'val'/'val_annotations.txt'\n",
"anno = dict(o.split('\\t')[:2] for o in path_anno.read_text().splitlines())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class TinyValDS(TinyDS):\n",
" def __getitem__(self, i): return self.files[i],anno[os.path.basename(self.files[i])]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vds = TinyValDS(path/'val')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class TfmDS:\n",
" def __init__(self, ds, tfmx=fc.noop, tfmy=fc.noop): self.ds,self.tfmx,self.tfmy = ds,tfmx,tfmy\n",
" def __len__(self): return len(self.ds)\n",
" def __getitem__(self, i):\n",
" x,y = self.ds[i]\n",
" return self.tfmx(x),self.tfmy(y)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"id2str = (path/'wnids.txt').read_text().splitlines()\n",
"str2id = {v:k for k,v in enumerate(id2str)}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"xmean,xstd = (tensor([0.47565, 0.40303, 0.31555]), tensor([0.28858, 0.24402, 0.26615]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def tfmx(x):\n",
" img = read_image(x, mode=ImageReadMode.RGB)/255\n",
" return (img-xmean[:,None,None])/xstd[:,None,None]\n",
"\n",
"def tfmy(y): return tensor(str2id[y])\n",
"\n",
"tfm_tds = TfmDS(tds, tfmx, tfmy)\n",
"tfm_vds = TfmDS(vds, tfmx, tfmy)\n",
"\n",
"def denorm(x): return (x*xstd[:,None,None]+xmean[:,None,None]).clip(0,1)\n",
"\n",
"all_synsets = [o.split('\\t') for o in (path/'words.txt').read_text().splitlines()]\n",
"synsets = {k:v.split(',', maxsplit=1)[0] for k,v in all_synsets if k in id2str}\n",
"\n",
"dls = DataLoaders(*get_dls(tfm_tds, tfm_vds, bs=bs, num_workers=8))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def tfm_batch(b, tfm_x=fc.noop, tfm_y = fc.noop): return tfm_x(b[0]),tfm_y(b[1])\n",
"\n",
"tfms = nn.Sequential(T.Pad(4), T.RandomCrop(64),\n",
" T.RandomHorizontalFlip(),\n",
" RandErase())\n",
"augcb = BatchTransformCB(partial(tfm_batch, tfm_x=tfms), on_val=False)\n",
"\n",
"act_gr = partial(GeneralRelu, leak=0.1, sub=0.4)\n",
"iw = partial(init_weights, leaky=0.1)\n",
"\n",
"nfs = (32,64,128,256,512,1024)\n",
"\n",
"def get_dropmodel(act=act_gr, nfs=nfs, norm=nn.BatchNorm2d, drop=0.1):\n",
" layers = [nn.Conv2d(3, nfs[0], 5, padding=2)]\n",
"# layers += [ResBlock(nfs[0], nfs[0], ks=3, stride=1, act=act, norm=norm)]\n",
" layers += [ResBlock(nfs[i], nfs[i+1], act=act, norm=norm, stride=2)\n",
" for i in range(len(nfs)-1)]\n",
" layers += [nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Dropout(drop)]\n",
" layers += [nn.Linear(nfs[-1], 200, bias=False), nn.BatchNorm1d(200)]\n",
" return nn.Sequential(*layers).apply(iw)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def res_blocks(n_bk, ni, nf, stride=1, ks=3, act=act_gr, norm=None):\n",
" return nn.Sequential(*[\n",
" ResBlock(ni if i==0 else nf, nf, stride=stride if i==n_bk-1 else 1, ks=ks, act=act, norm=norm)\n",
" for i in range(n_bk)])\n",
"\n",
"nbks = (3,2,2,1,1)\n",
"\n",
"def get_dropmodel(act=act_gr, nfs=nfs, nbks=nbks, norm=nn.BatchNorm2d, drop=0.2):\n",
" layers = [ResBlock(3, nfs[0], ks=5, stride=1, act=act, norm=norm)]\n",
" layers += [res_blocks(nbks[i], nfs[i], nfs[i+1], act=act, norm=norm, stride=2)\n",
" for i in range(len(nfs)-1)]\n",
" layers += [nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Dropout(drop)]\n",
" layers += [nn.Linear(nfs[-1], 200, bias=False), nn.BatchNorm1d(200)]\n",
" return nn.Sequential(*layers).apply(iw)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"opt_func = partial(optim.AdamW, eps=1e-5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"metrics = MetricsCB(accuracy=MulticlassAccuracy())\n",
"cbs = [DeviceCB(), metrics, ProgressCB(plot=True), MixedPrecision()]\n",
"\n",
"epochs = 25\n",
"lr = 3e-2\n",
"tmax = epochs * len(dls.train)\n",
"sched = partial(lr_scheduler.OneCycleLR, max_lr=lr, total_steps=tmax)\n",
"xtra = [BatchSchedCB(sched), augcb]\n",
"learn = Learner(get_dropmodel(), dls, F.cross_entropy, lr=lr, cbs=cbs+xtra, opt_func=opt_func)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"aug_tfms = nn.Sequential(T.Pad(4), T.RandomCrop(64),\n",
" T.RandomHorizontalFlip(),\n",
" T.TrivialAugmentWide())\n",
"\n",
"norm_tfm = T.Normalize(xmean, xstd)\n",
"erase_tfm = RandErase()\n",
"\n",
"from PIL import Image\n",
"\n",
"def tfmx(x, aug=False):\n",
" x = Image.open(x).convert('RGB')\n",
" if aug: x = aug_tfms(x)\n",
" x = TF.to_tensor(x)\n",
" x = norm_tfm(x)\n",
" if aug: x = erase_tfm(x[None])[0]\n",
" return x\n",
"\n",
"tfm_tds = TfmDS(tds, partial(tfmx, aug=True), tfmy)\n",
"tfm_vds = TfmDS(vds, tfmx, tfmy)\n",
"\n",
"dls = DataLoaders(*get_dls(tfm_tds, tfm_vds, bs=bs, num_workers=8))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def conv(ni, nf, ks=3, stride=1, act=nn.ReLU, norm=None, bias=True):\n",
" layers = []\n",
" if norm: layers.append(norm(ni))\n",
" if act : layers.append(act())\n",
" layers.append(nn.Conv2d(ni, nf, stride=stride, kernel_size=ks, padding=ks//2, bias=bias))\n",
" return nn.Sequential(*layers)\n",
"\n",
"def _conv_block(ni, nf, stride, act=act_gr, norm=None, ks=3):\n",
" return nn.Sequential(conv(ni, nf, stride=1 , act=act, norm=norm, ks=ks),\n",
" conv(nf, nf, stride=stride, act=act, norm=norm, ks=ks))\n",
"\n",
"class ResBlock(nn.Module):\n",
" def __init__(self, ni, nf, stride=1, ks=3, act=act_gr, norm=None):\n",
" super().__init__()\n",
" self.convs = _conv_block(ni, nf, stride, act=act, ks=ks, norm=norm)\n",
" self.idconv = fc.noop if ni==nf else conv(ni, nf, ks=1, stride=1, act=None, norm=norm)\n",
" self.pool = fc.noop if stride==1 else nn.AvgPool2d(2, ceil_mode=True)\n",
"\n",
" def forward(self, x): return self.convs(x) + self.idconv(self.pool(x))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_dropmodel(act=act_gr, nfs=nfs, nbks=nbks, norm=nn.BatchNorm2d, drop=0.2):\n",
" layers = [nn.Conv2d(3, nfs[0], 5, padding=2)]\n",
" layers += [res_blocks(nbks[i], nfs[i], nfs[i+1], act=act, norm=norm, stride=2)\n",
" for i in range(len(nfs)-1)]\n",
" layers += [act_gr(), norm(nfs[-1]), nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Dropout(drop)]\n",
" layers += [nn.Linear(nfs[-1], 200, bias=False), nn.BatchNorm1d(200)]\n",
" return nn.Sequential(*layers).apply(iw)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"epochs = 50\n",
"lr = 0.1\n",
"tmax = epochs * len(dls.train)\n",
"sched = partial(lr_scheduler.OneCycleLR, max_lr=lr, total_steps=tmax)\n",
"xtra = [BatchSchedCB(sched)]\n",
"model = get_dropmodel(nbks=(1,2,4,2,2), nfs=(32, 64, 128, 512, 768, 1024), drop=0.1)\n",
"learn = Learner(model, dls, F.cross_entropy, lr=lr, cbs=cbs+xtra, opt_func=opt_func)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
" \n",
" \n",
" accuracy | \n",
" loss | \n",
" epoch | \n",
" train | \n",
"
\n",
" \n",
" \n",
" \n",
" 0.022 | \n",
" 5.068 | \n",
" 0 | \n",
" train | \n",
"
\n",
" \n",
" 0.037 | \n",
" 4.833 | \n",
" 0 | \n",
" eval | \n",
"
\n",
" \n",
" 0.046 | \n",
" 4.766 | \n",
" 1 | \n",
" train | \n",
"
\n",
" \n",
" 0.065 | \n",
" 4.545 | \n",
" 1 | \n",
" eval | \n",
"
\n",
" \n",
" 0.072 | \n",
" 4.501 | \n",
" 2 | \n",
" train | \n",
"
\n",
" \n",
" 0.078 | \n",
" 4.342 | \n",
" 2 | \n",
" eval | \n",
"
\n",
" \n",
" 0.099 | \n",
" 4.268 | \n",
" 3 | \n",
" train | \n",
"
\n",
" \n",
" 0.135 | \n",
" 3.958 | \n",
" 3 | \n",
" eval | \n",
"
\n",
" \n",
" 0.137 | \n",
" 4.010 | \n",
" 4 | \n",
" train | \n",
"
\n",
" \n",
" 0.134 | \n",
" 4.026 | \n",
" 4 | \n",
" eval | \n",
"
\n",
" \n",
" 0.166 | \n",
" 3.801 | \n",
" 5 | \n",
" train | \n",
"
\n",
" \n",
" 0.162 | \n",
" 3.899 | \n",
" 5 | \n",
" eval | \n",
"
\n",
" \n",
" 0.195 | \n",
" 3.635 | \n",
" 6 | \n",
" train | \n",
"
\n",
" \n",
" 0.212 | \n",
" 3.536 | \n",
" 6 | \n",
" eval | \n",
"
\n",
" \n",
" 0.214 | \n",
" 3.503 | \n",
" 7 | \n",
" train | \n",
"
\n",
" \n",
" 0.242 | \n",
" 3.391 | \n",
" 7 | \n",
" eval | \n",
"
\n",
" \n",
" 0.237 | \n",
" 3.382 | \n",
" 8 | \n",
" train | \n",
"
\n",
" \n",
" 0.260 | \n",
" 3.325 | \n",
" 8 | \n",
" eval | \n",
"
\n",
" \n",
" 0.252 | \n",
" 3.293 | \n",
" 9 | \n",
" train | \n",
"
\n",
" \n",
" 0.300 | \n",
" 3.074 | \n",
" 9 | \n",
" eval | \n",
"
\n",
" \n",
" 0.269 | \n",
" 3.202 | \n",
" 10 | \n",
" train | \n",
"
\n",
" \n",
" 0.287 | \n",
" 3.198 | \n",
" 10 | \n",
" eval | \n",
"
\n",
" \n",
" 0.286 | \n",
" 3.118 | \n",
" 11 | \n",
" train | \n",
"
\n",
" \n",
" 0.295 | \n",
" 3.080 | \n",
" 11 | \n",
" eval | \n",
"
\n",
" \n",
" 0.296 | \n",
" 3.055 | \n",
" 12 | \n",
" train | \n",
"
\n",
" \n",
" 0.307 | \n",
" 3.070 | \n",
" 12 | \n",
" eval | \n",
"
\n",
" \n",
" 0.309 | \n",
" 2.984 | \n",
" 13 | \n",
" train | \n",
"
\n",
" \n",
" 0.323 | \n",
" 3.021 | \n",
" 13 | \n",
" eval | \n",
"
\n",
" \n",
" 0.319 | \n",
" 2.931 | \n",
" 14 | \n",
" train | \n",
"
\n",
" \n",
" 0.334 | \n",
" 2.866 | \n",
" 14 | \n",
" eval | \n",
"
\n",
" \n",
" 0.333 | \n",
" 2.868 | \n",
" 15 | \n",
" train | \n",
"
\n",
" \n",
" 0.312 | \n",
" 2.970 | \n",
" 15 | \n",
" eval | \n",
"
\n",
" \n",
" 0.343 | \n",
" 2.813 | \n",
" 16 | \n",
" train | \n",
"
\n",
" \n",
" 0.283 | \n",
" 3.314 | \n",
" 16 | \n",
" eval | \n",
"
\n",
" \n",
" 0.353 | \n",
" 2.762 | \n",
" 17 | \n",
" train | \n",
"
\n",
" \n",
" 0.368 | \n",
" 2.690 | \n",
" 17 | \n",
" eval | \n",
"
\n",
" \n",
" 0.362 | \n",
" 2.713 | \n",
" 18 | \n",
" train | \n",
"
\n",
" \n",
" 0.329 | \n",
" 2.986 | \n",
" 18 | \n",
" eval | \n",
"
\n",
" \n",
" 0.368 | \n",
" 2.680 | \n",
" 19 | \n",
" train | \n",
"
\n",
" \n",
" 0.374 | \n",
" 2.743 | \n",
" 19 | \n",
" eval | \n",
"
\n",
" \n",
" 0.377 | \n",
" 2.635 | \n",
" 20 | \n",
" train | \n",
"
\n",
" \n",
" 0.372 | \n",
" 2.705 | \n",
" 20 | \n",
" eval | \n",
"
\n",
" \n",
" 0.386 | \n",
" 2.587 | \n",
" 21 | \n",
" train | \n",
"
\n",
" \n",
" 0.379 | \n",
" 2.755 | \n",
" 21 | \n",
" eval | \n",
"
\n",
" \n",
" 0.394 | \n",
" 2.551 | \n",
" 22 | \n",
" train | \n",
"
\n",
" \n",
" 0.378 | \n",
" 2.689 | \n",
" 22 | \n",
" eval | \n",
"
\n",
" \n",
" 0.402 | \n",
" 2.505 | \n",
" 23 | \n",
" train | \n",
"
\n",
" \n",
" 0.396 | \n",
" 2.563 | \n",
" 23 | \n",
" eval | \n",
"
\n",
" \n",
" 0.411 | \n",
" 2.469 | \n",
" 24 | \n",
" train | \n",
"
\n",
" \n",
" 0.429 | \n",
" 2.437 | \n",
" 24 | \n",
" eval | \n",
"
\n",
" \n",
" 0.420 | \n",
" 2.416 | \n",
" 25 | \n",
" train | \n",
"
\n",
" \n",
" 0.423 | \n",
" 2.477 | \n",
" 25 | \n",
" eval | \n",
"
\n",
" \n",
" 0.431 | \n",
" 2.366 | \n",
" 26 | \n",
" train | \n",
"
\n",
" \n",
" 0.406 | \n",
" 2.596 | \n",
" 26 | \n",
" eval | \n",
"
\n",
" \n",
" 0.439 | \n",
" 2.328 | \n",
" 27 | \n",
" train | \n",
"
\n",
" \n",
" 0.403 | \n",
" 2.525 | \n",
" 27 | \n",
" eval | \n",
"
\n",
" \n",
" 0.449 | \n",
" 2.273 | \n",
" 28 | \n",
" train | \n",
"
\n",
" \n",
" 0.424 | \n",
" 2.490 | \n",
" 28 | \n",
" eval | \n",
"
\n",
" \n",
" 0.462 | \n",
" 2.215 | \n",
" 29 | \n",
" train | \n",
"
\n",
" \n",
" 0.477 | \n",
" 2.181 | \n",
" 29 | \n",
" eval | \n",
"
\n",
" \n",
" 0.471 | \n",
" 2.172 | \n",
" 30 | \n",
" train | \n",
"
\n",
" \n",
" 0.474 | \n",
" 2.224 | \n",
" 30 | \n",
" eval | \n",
"
\n",
" \n",
" 0.486 | \n",
" 2.103 | \n",
" 31 | \n",
" train | \n",
"
\n",
" \n",
" 0.518 | \n",
" 2.009 | \n",
" 31 | \n",
" eval | \n",
"
\n",
" \n",
" 0.502 | \n",
" 2.027 | \n",
" 32 | \n",
" train | \n",
"
\n",
" \n",
" 0.495 | \n",
" 2.119 | \n",
" 32 | \n",
" eval | \n",
"
\n",
" \n",
" 0.513 | \n",
" 1.969 | \n",
" 33 | \n",
" train | \n",
"
\n",
" \n",
" 0.478 | \n",
" 2.217 | \n",
" 33 | \n",
" eval | \n",
"
\n",
" \n",
" 0.529 | \n",
" 1.890 | \n",
" 34 | \n",
" train | \n",
"
\n",
" \n",
" 0.516 | \n",
" 2.058 | \n",
" 34 | \n",
" eval | \n",
"
\n",
" \n",
" 0.544 | \n",
" 1.827 | \n",
" 35 | \n",
" train | \n",
"
\n",
" \n",
" 0.532 | \n",
" 1.925 | \n",
" 35 | \n",
" eval | \n",
"
\n",
" \n",
" 0.565 | \n",
" 1.731 | \n",
" 36 | \n",
" train | \n",
"
\n",
" \n",
" 0.557 | \n",
" 1.866 | \n",
" 36 | \n",
" eval | \n",
"
\n",
" \n",
" 0.580 | \n",
" 1.662 | \n",
" 37 | \n",
" train | \n",
"
\n",
" \n",
" 0.557 | \n",
" 1.877 | \n",
" 37 | \n",
" eval | \n",
"
\n",
" \n",
" 0.603 | \n",
" 1.565 | \n",
" 38 | \n",
" train | \n",
"
\n",
" \n",
" 0.585 | \n",
" 1.726 | \n",
" 38 | \n",
" eval | \n",
"
\n",
" \n",
" 0.623 | \n",
" 1.471 | \n",
" 39 | \n",
" train | \n",
"
\n",
" \n",
" 0.590 | \n",
" 1.725 | \n",
" 39 | \n",
" eval | \n",
"
\n",
" \n",
" 0.646 | \n",
" 1.369 | \n",
" 40 | \n",
" train | \n",
"
\n",
" \n",
" 0.602 | \n",
" 1.683 | \n",
" 40 | \n",
" eval | \n",
"
\n",
" \n",
" 0.671 | \n",
" 1.263 | \n",
" 41 | \n",
" train | \n",
"
\n",
" \n",
" 0.607 | \n",
" 1.690 | \n",
" 41 | \n",
" eval | \n",
"
\n",
" \n",
" 0.696 | \n",
" 1.169 | \n",
" 42 | \n",
" train | \n",
"
\n",
" \n",
" 0.616 | \n",
" 1.649 | \n",
" 42 | \n",
" eval | \n",
"
\n",
" \n",
" 0.720 | \n",
" 1.069 | \n",
" 43 | \n",
" train | \n",
"
\n",
" \n",
" 0.629 | \n",
" 1.608 | \n",
" 43 | \n",
" eval | \n",
"
\n",
" \n",
" 0.742 | \n",
" 0.983 | \n",
" 44 | \n",
" train | \n",
"
\n",
" \n",
" 0.634 | \n",
" 1.594 | \n",
" 44 | \n",
" eval | \n",
"
\n",
" \n",
" 0.761 | \n",
" 0.912 | \n",
" 45 | \n",
" train | \n",
"
\n",
" \n",
" 0.639 | \n",
" 1.579 | \n",
" 45 | \n",
" eval | \n",
"
\n",
" \n",
" 0.779 | \n",
" 0.847 | \n",
" 46 | \n",
" train | \n",
"
\n",
" \n",
" 0.642 | \n",
" 1.567 | \n",
" 46 | \n",
" eval | \n",
"
\n",
" \n",
" 0.791 | \n",
" 0.801 | \n",
" 47 | \n",
" train | \n",
"
\n",
" \n",
" 0.645 | \n",
" 1.558 | \n",
" 47 | \n",
" eval | \n",
"
\n",
" \n",
" 0.797 | \n",
" 0.774 | \n",
" 48 | \n",
" train | \n",
"
\n",
" \n",
" 0.647 | \n",
" 1.553 | \n",
" 48 | \n",
" eval | \n",
"
\n",
" \n",
" 0.802 | \n",
" 0.766 | \n",
" 49 | \n",
" train | \n",
"
\n",
" \n",
" 0.644 | \n",
" 1.556 | \n",
" 49 | \n",
" eval | \n",
"
\n",
" \n",
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "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",
"text/plain": [
"