Pascal VOC dataseを用いて顔検出を学習する②(学習編)

前回の続きです。
touch-sp.hatenablog.com
データ準備はGPU非搭載のパソコンで行ったが、学習はGPU搭載パソコンで行った。

Windows10 Pro
NVIDIA GeForce GTX1080
Python 3.7.9
CUDA 10.1

環境構築

「mxnet-cu101」と「gluoncv」のみインストールした。その他は勝手についてきた。
cuDNNは別途入れていない。(こちらを参照)

pip install mxnet-cu101==1.7.0 -f https://dist.mxnet.io/python/cu101
pip install gluoncv

certifi==2020.6.20
chardet==3.0.4
cycler==0.10.0
gluoncv==0.8.0
graphviz==0.8.4
idna==2.6
kiwisolver==1.2.0
matplotlib==3.3.2
mxnet-cu101==1.7.0
numpy==1.16.6
Pillow==8.0.1
portalocker==2.0.0
pyparsing==2.4.7
python-dateutil==2.8.1
pywin32==228
requests==2.18.4
scipy==1.5.3
six==1.15.0
tqdm==4.50.2
urllib3==1.22

学習のためのPythonスクリプト

import time
import mxnet as mx
from mxnet import gluon, autograd
from mxnet.gluon.data import DataLoader

from gluoncv import model_zoo
from gluoncv.data import VOCDetection
from gluoncv.data.transforms import presets
from gluoncv.data.batchify import Tuple, Stack
from gluoncv.loss import SSDMultiBoxLoss

ctx = [mx.gpu()]

classes = ['face']

net = model_zoo.get_model('ssd_512_mobilenet1.0_voc', pretrained=True, ctx = ctx[0], root='./models')
net.reset_class(classes)
net.hybridize()

x = mx.nd.zeros(shape=(1, 3, 512, 512),ctx=ctx[0])
with autograd.train_mode():
    _, _, anchors = net(x)

batch_size = 16
num_workers = 0
epochs = 5

width, height = 512, 512  
train_transform = presets.ssd.SSDDefaultTrainTransform(width, height, anchors.as_in_context(mx.cpu()))
batchify_fn = Tuple(Stack(), Stack(), Stack())

VOCDetection.CLASSES = ['head']
train_dataset = VOCDetection(root='VOCdevkit',splits=((2012, 'train'),))

train_loader = DataLoader(
    train_dataset.transform(train_transform),
    batch_size,
    shuffle=True,
    batchify_fn=batchify_fn,
    last_batch='rollover',
    num_workers=num_workers)

mbox_loss = SSDMultiBoxLoss()
ce_metric = mx.metric.Loss('CrossEntropy')
smoothl1_metric = mx.metric.Loss('SmoothL1')

trainer = gluon.Trainer(
    net.collect_params(), 'sgd',
    {'learning_rate': 0.001, 'wd': 0.0005, 'momentum': 0.9})

for epoch in range(epochs):
    ce_metric.reset()
    smoothl1_metric.reset()
    tic = time.time()
    btic = time.time()
    for i, batch in enumerate(train_loader):
        batch_size = batch[0].shape[0]
        data = gluon.utils.split_and_load(batch[0], ctx_list=ctx, batch_axis=0)
        cls_targets = gluon.utils.split_and_load(batch[1], ctx_list=ctx, batch_axis=0)
        box_targets = gluon.utils.split_and_load(batch[2], ctx_list=ctx, batch_axis=0)
        with autograd.record():
            cls_preds = []
            box_preds = []
            for x in data:
                cls_pred, box_pred, _ = net(x)
                cls_preds.append(cls_pred)
                box_preds.append(box_pred)
            sum_loss, cls_loss, box_loss = mbox_loss(
                cls_preds, box_preds, cls_targets, box_targets)
            autograd.backward(sum_loss)
        trainer.step(1)
        ce_metric.update(0, [l * batch_size for l in cls_loss])
        smoothl1_metric.update(0, [l * batch_size for l in box_loss])
        name1, loss1 = ce_metric.get()
        name2, loss2 = smoothl1_metric.get()
        if i % 20 == 0:
            print('[Epoch {}][Batch {}], Speed: {:.3f} samples/sec, {}={:.3f}, {}={:.3f}'.format(
                epoch, i, batch_size/(time.time()-btic), name1, loss1, name2, loss2))
        btic = time.time()

net.save_parameters('ssd_512_mobilenet1.0_face.params')

上記を実行すると「ssd_512_mobilenet1.0_face.params」というファイルが保存される。

結果の確認

from matplotlib import pyplot as plt
from gluoncv import model_zoo, data, utils

url = 'https://raw.githubusercontent.com/dmlc/web-data/master/gluoncv/segmentation/mhpv1_examples/1.jpg'
filename = 'mhp_v1_example.jpg'
utils.download(url, filename)

classes = ['face']
net = model_zoo.get_model('ssd_512_mobilenet1.0_voc', pretrained=True, root='./models')
net.reset_class(classes)
net.load_parameters('ssd_512_mobilenet1.0_face.params')

x, image = data.transforms.presets.ssd.load_test(filename, 512)
cid, score, bbox = net(x)
ax = utils.viz.plot_bbox(image, bbox[0], score[0], cid[0], class_names=classes)

plt.axis('off')
plt.show()

f:id:touch-sp:20201024200644p:plain:w300
うまくいったと思う。
自分の環境では学習は5分程度で終了した。

Pascal VOC dataseを用いて顔検出を学習する①(データ準備編)

はじめに

以前も同様のことをした。
touch-sp.hatenablog.com
今回はデータをPascal VOC formatで扱ってみる。

データのダウンロードと解凍

host.robots.ox.ac.uk
上のサイトから「VOCtrainval_11-May-2012.tar」をダウンロード。
ダウンロードしたファイルは解凍が必要であるが今回はPythonを使った。
Windowsでも動作する)

import tarfile

data_file = 'VOCtrainval_11-May-2012.tar'
with tarfile.open(data_file) as tar:
    tar.extractall(path='.')

データの加工

解凍したファイルのフォルダ構造は以下のようになっている。

VOCdevkit
└─VOC2012
    ├─Annotations
    ├─ImageSets
    │  ├─Action
    │  ├─Layout
    │  ├─Main
    │  └─Segmentation
    ├─JPEGImages
    ├─SegmentationClass
    └─SegmentationObject

Annotationsフォルダ内にはxmlファイルがはいっている。その中の一つを見てみる(「2007_002079.xml」)
f:id:touch-sp:20201023131021p:plain
今回は「object/part」下の「head」のみを学習データとして使う。
「object」下の「person」にアクセスするにはgluoncvのVOCDetectionを使えばよい。
「object/part」下の「head」に簡単にアクセスする方法が見つけられなかった。
今回は強引にXMLファイルを書き換えることにした。こうすることによってVOCDetectionが使える。

import glob
import os
import xml.etree.cElementTree as ET

root1 = 'VOCdevkit'
root2 = 'VOC2012'

os.mkdir('Annotations')
os.mkdir('Main')

all_xml_pass = glob.glob(os.path.join(root1, root2, 'Annotations','*.xml'))
all_xml_filename = [os.path.split(f)[1] for f in all_xml_pass]

for each_xml_pass in all_xml_pass:
    xml_filename = os.path.split(each_xml_pass)[1]
    
    tree = ET.parse(each_xml_pass)
    root = tree.getroot()

    bndbox_all = []
    for child in root.findall('object/part'):
        bndbox = []
        if child.find('name').text == 'head':
            bndbox.append(child.find('bndbox/xmin').text)
            bndbox.append(child.find('bndbox/ymin').text)
            bndbox.append(child.find('bndbox/xmax').text)
            bndbox.append(child.find('bndbox/ymax').text)
        if len(bndbox)>0:
            bndbox_all.append(bndbox)
    
    if len(bndbox_all)>0:

        jpg_filename = xml_filename.replace('xml', 'jpg')
        new_root = ET.Element('annotation')
        
        new_filename = ET.SubElement(new_root, 'filename')
        new_filename.text = jpg_filename

        Size = ET.SubElement(new_root, 'size')
        Width = ET.SubElement(Size, 'width')
        Height = ET.SubElement(Size, 'height')
        Depth = ET.SubElement(Size, 'depth')

        Width.text = root.find('size/width').text
        Height.text = root.find('size/height').text
        Depth.text = root.find('size/depth').text

        for new_budbox in bndbox_all:
            Object = ET.SubElement(new_root, 'object')
            
            Name = ET.SubElement(Object, 'name')
            Name.text = 'head'

            Difficult = ET.SubElement(Object, 'difficult')
            Difficult.text = '0'

            Bndbox = ET.SubElement(Object, 'bndbox')
            Xmin = ET.SubElement(Bndbox, 'xmin')
            Ymin = ET.SubElement(Bndbox, 'ymin')
            Xmax = ET.SubElement(Bndbox, 'xmax')
            Ymax = ET.SubElement(Bndbox, 'ymax')

            Xmin.text = new_budbox[0]
            Ymin.text = new_budbox[1]
            Xmax.text = new_budbox[2]
            Ymax.text = new_budbox[3]

        new_tree = ET.ElementTree(new_root) 

        new_tree.write(os.path.join('Annotations',xml_filename)) 

new_xml_files = glob.glob('Annotations/*.xml')
train_data = [os.path.split(f)[1].replace('.xml','') for f in new_xml_files]

text = "\n".join(train_data)
with open(os.path.join('Main', 'train.txt'), "w") as f:
    f.write(text)      

「Annotations」フォルダと「Main」フォルダが新規に作成されるため「VOCdevkit」フォルダ内の同名フォルダと入れ替える。
新しく作成したXMLファイルがこちら。
f:id:touch-sp:20201023130618p:plain:w300

データの確認

from gluoncv import utils
from gluoncv.data import VOCDetection
from matplotlib import pyplot as plt

VOCDetection.CLASSES = ['head']
train_dataset = VOCDetection(root='VOCdevkit', splits=((2012,'train'),))

train_image, train_label = train_dataset[10]

bounding_boxes = train_label[:, :4]
class_ids = train_label[:, 4:5]

utils.viz.plot_bbox(train_image.asnumpy(), bounding_boxes, scores=None,
                    labels=class_ids, class_names=['face'])

plt.show()

f:id:touch-sp:20201022232937p:plain

動作環境

Windows 10 
Python 3.7.9

certifi==2020.6.20
chardet==3.0.4
cycler==0.10.0
gluoncv==0.8.0
graphviz==0.8.4
idna==2.6
kiwisolver==1.2.0
matplotlib==3.3.2
mxnet==1.7.0
numpy==1.16.6
Pillow==8.0.0
portalocker==2.0.0
pyparsing==2.4.7
python-dateutil==2.8.1
pywin32==228
requests==2.18.4
scipy==1.5.3
six==1.15.0
tqdm==4.50.2
urllib3==1.2

インストールしたのは「mxnet」と「gluoncv」のみ。その他は勝手についてくる。

pip install mxnet==1.7.0 -f https://dist.mxnet.io/python/cpu
pip install gluoncv

写真に写った物体の数を数える

はじめに

Windowsで実行した過去のスクリプトを今回WSL2上のUbuntuで動作検証しただけです。
touch-sp.hatenablog.com
f:id:touch-sp:20201018130207j:plain
たとえば上の写真に車が何台写っているかを数えてみる。

Pythonスクリプト

import mxnet as mx
from gluoncv import model_zoo, data, utils

url = 'https://cdn-ak.f.st-hatena.com/images/fotolife/t/touch-sp/20190814/20190814122423.jpg'
filename = 'cars.jpg'
utils.download(url, filename)

net = model_zoo.get_model('faster_rcnn_fpn_resnet101_v1d_coco', pretrained=True, root='./models')
net.reset_class(['car'], reuse_weights=['car'])

x, img = data.transforms.presets.rcnn.load_test(filename)

class_IDs, scores, bounding_boxs = net(x)

count = int(mx.nd.sum(scores[0]>0.5).asscalar())    

print(count)

結果

3

うまくカウントできている。
GluonCVを使えば非常に短いスクリプトでカウントできる。
たったの11行。しかもそのうち3行は画像の準備(ダウンロード)。
importとprintを除けば実質5行でカウントできている。

次のような警告がでるが無視して問題なし。

UserWarning: Cannot decide type for the following arguments. Consider providing them as input:
        data: None
  input_sym_arg_type = in_param.infer_type()[0]

今回の動作環境

Ubuntu 18.04LTS (WSL2)
Python 3.7.5

certifi==2020.6.20
chardet==3.0.4
cycler==0.10.0
gluoncv==0.9.0b20201017
graphviz==0.8.4
idna==2.10
kiwisolver==1.2.0
matplotlib==3.3.2
mxnet==1.9.0b20201015
numpy==1.19.2
Pillow==8.0.0
pkg-resources==0.0.0
portalocker==2.0.0
pyparsing==3.0.0a2
python-dateutil==2.8.1
requests==2.24.0
scipy==1.5.3
six==1.15.0
tqdm==4.50.2
urllib3==1.25.10

インストールしたのは「mxnet」と「gluoncv」のみ。その他は勝手についてきた。

pip install mxnet==1.9.0b20201015 -f https://dist.mxnet.io/python/cpu
pip install gluoncv --pre

何がカウントできるか?

以下の3行を実行してみる。

from gluoncv import model_zoo
net = model_zoo.get_model('faster_rcnn_fpn_resnet101_v1d_coco', pretrained=True, root='./models')
print(net.classes)

すると以下のように出力される。

['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 
'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush']

80種類のカウントが可能である。
Pythonスクリプトの'car'の部分を任意に変更すればよい。

net.reset_class(['car'], reuse_weights=['car'])

補足(Ubuntu18.04にPython3.7を入れる方法)

こちらを参照して下さい。
touch-sp.hatenablog.com

Pose Estimationの結果をテキストで表示する

Pythonスクリプト

from gluoncv import model_zoo, data, utils
from gluoncv.data.transforms.pose import detector_to_simple_pose, heatmap_to_coord

detector = model_zoo.get_model('yolo3_mobilenet1.0_coco', pretrained=True, root='./model')
pose_net = model_zoo.get_model('simple_pose_resnet18_v1b', pretrained=True, root='./model')
detector.reset_class(["person"], reuse_weights=['person'])

url = 'https://raw.githubusercontent.com/dmlc/web-data/master/gluoncv/segmentation/voc_examples/1.jpg'
filename = 'sample.jpg'
utils.download(url, filename)

x, img = data.transforms.presets.ssd.load_test(filename, short=512)
class_IDs, scores, bounding_boxs = detector(x)
pose_input, upscale_bbox = detector_to_simple_pose(img, class_IDs, scores, bounding_boxs)
predicted_heatmap = pose_net(pose_input)
pred_coords, confidence = heatmap_to_coord(predicted_heatmap, upscale_bbox)

from PIL import Image, ImageDraw, ImageFont
font = ImageFont.truetype("arial.ttf", 20)

pil_image = Image.fromarray(img)
draw = ImageDraw.Draw(pil_image)
keypoints = data.mscoco.keypoints.COCOKeyPoints.KEYPOINTS
for keypoint_id in range(len(keypoints)):
    pred = pred_coords[:,keypoint_id,:]
    for i in range(pred.shape[0]):
        if (confidence[i,keypoint_id,:] > 0.2) == 1:
            draw.text(pred[i,:].asnumpy(),text=keypoints[keypoint_id], fill='red', font=font)
pil_image.save('result.png')

結果

f:id:touch-sp:20201017160024p:plain:w400

動作環境

Windows 10
Python 3.7.8

インストールしたのは「mxnet」と「gluoncv」のみ。その他は勝手についてきた。

pip install mxnet==1.7.0 -f https://dist.mxnet.io/python/cpu
pip install gluoncv --pre

certifi==2020.6.20
chardet==3.0.4
cycler==0.10.0
gluoncv==0.9.0b20201016
graphviz==0.8.4
idna==2.6
kiwisolver==1.2.0
matplotlib==3.3.2
mxnet==1.7.0
numpy==1.16.6
Pillow==8.0.0
portalocker==2.0.0
pyparsing==3.0.0a2
python-dateutil==2.8.1
pywin32==228
requests==2.18.4
scipy==1.5.2
six==1.15.0
tqdm==4.50.2
urllib3==1.22

2020年10月18日追記

WSL2上のUbuntuでも実行可能であった。(Python 3.7.5)
ただしフォントの変更は出来なかった。
以下の部分でエラーを吐き出す。

font = ImageFont.truetype("arial.ttf", 20)

certifi==2020.6.20
chardet==3.0.4
cycler==0.10.0
gluoncv==0.9.0b20201017
graphviz==0.8.4
idna==2.10
kiwisolver==1.2.0
matplotlib==3.3.2
mxnet==1.9.0b20201015
numpy==1.19.2
Pillow==8.0.0
pkg-resources==0.0.0
portalocker==2.0.0
pyparsing==3.0.0a2
python-dateutil==2.8.1
requests==2.24.0
scipy==1.5.3
six==1.15.0
tqdm==4.50.2
urllib3==1.25.10

WSL2上のMXNetで超解像(写真をきれいに拡大)

過去のスクリプトを動作検証しただけです。
touch-sp.hatenablog.com
一番新しいベータ版(MXNet2.0)をインストールしてみた。

Ubuntu 18.04LTS
Python 3.7.5

pip install mxnet==2.0.0b20201015 -f https://dist.mxnet.io/python/cpu
pip install pillow

以上の二つだけインストールしたら他は勝手についてくる。
Pillowもいつの間にか8.0.0にバージョンアップしていた。

certifi==2020.6.20
chardet==3.0.4
graphviz==0.8.4
idna==2.10
mxnet==2.0.0b20201015
numpy==1.19.2
Pillow==8.0.0
pkg-resources==0.0.0
requests==2.24.0
urllib3==1.25.10

Pythonスクリプト

import mxnet as mx
from mxnet import image, gluon

gluon.utils.download('https://s3.amazonaws.com/onnx-mxnet/examples/super_res_input.jpg')

img = image.imread('super_res_input.jpg').astype('float32')/255
img = mx.nd.transpose(img, (2,0,1))

gluon.utils.download('https://raw.githubusercontent.com/WolframRhodium/Super-Resolution-Zoo/master/ARAN/aran_c0_s1_x4-symbol.json')
gluon.utils.download('https://raw.githubusercontent.com/WolframRhodium/Super-Resolution-Zoo/master/ARAN/aran_c0_s1_x4-0000.params')

net = gluon.nn.SymbolBlock.imports("aran_c0_s1_x4-symbol.json",['data'], "aran_c0_s1_x4-0000.params")
output = net(img.expand_dims(0))
output = mx.nd.squeeze(output)
output = (mx.nd.transpose(output, (1,2,0))*255).astype('uint8')

from PIL import Image
img = Image.fromarray(output.asnumpy())
img.save('ARAN_4x.jpg')

結果

mxnetは2.0になると現在の1.xから大きく変わるらしいが、4倍の超解像は全く問題なく動いた。
f:id:touch-sp:20201016122209j:plain:w128
f:id:touch-sp:20201016122247j:plain:w512

WSL2で最新のAutoGluonを使う(2020年10月15日)

以下のコマンドで簡単に最新バージョン(ベータ版)がインストールできる。
ベータ版は毎日更新されている。

pip install autogluon --pre

0.0.15b20201014以降で仕様が変わっているので前のスクリプトは修正が必要になっている。

環境

Ubuntu 18.04(WSL2)
Python 3.7.5 

バージョン

インストールしたのは「mxnet」と「autogluon」と「bokeh」の三つのみ。
その他は勝手についてきた。

pip install mxnet
pip install autogluon --pre
pip install bokeh==2.0.1

attrs==20.2.0
autogluon==0.0.15b20201014
autogluon-contrib-nlp==0.0.1b20201009
autogluon.core==0.0.15b20201014      
autogluon.extra==0.0.15b20201014     
autogluon.mxnet==0.0.15b20201014     
autogluon.tabular==0.0.15b20201014   
autogluon.text==0.0.15b20201014      
autogluon.vision==0.0.15b20201014
bcrypt==3.2.0
bokeh==2.0.1
boto3==1.15.16
botocore==1.18.16
catboost==0.24.2
certifi==2020.6.20
cffi==1.14.3
chardet==3.0.4
click==7.1.2
cloudpickle==1.6.0
ConfigSpace==0.4.10
contextvars==2.4
cryptography==3.1.1
cycler==0.10.0
Cython==3.0a6
dask==2.30.0
decorator==4.4.2
distributed==2.30.0
fastparquet==0.4.1
flake8==3.8.4
gluoncv==0.9.0b20201014
graphviz==0.8.4
HeapDict==1.0.1
idna==2.10
immutables==0.14
importlib-metadata==2.0.0
iniconfig==1.1.1
Jinja2==2.11.2
jmespath==0.10.0
joblib==0.17.0
kiwisolver==1.2.0
liac-arff==2.5.0
lightgbm==3.0.0
llvmlite==0.34.0
MarkupSafe==1.1.1
matplotlib==3.3.2
mccabe==0.6.1
msgpack==1.0.0
mxnet==1.7.0.post1
networkx==2.5
numba==0.51.2
numpy==1.19.2
openml==0.10.2
packaging==20.4
pandas==1.1.3
paramiko==2.7.2
Pillow==6.2.1
pkg-resources==0.0.0
plotly==4.11.0
pluggy==0.13.1
portalocker==2.0.0
protobuf==3.13.0
psutil==5.7.0
py==1.9.0
pyaml==20.4.0
pyarrow==1.0.0
pycodestyle==2.6.0
pycparser==2.20
pyflakes==2.2.0
PyNaCl==1.4.0
pyparsing==3.0.0a2
pytest==6.1.1
python-dateutil==2.8.1
pytz==2020.1
PyYAML==5.3.1
regex==2020.10.11
requests==2.24.0
retrying==1.3.3
s3transfer==0.3.3
sacrebleu==1.4.14
sacremoses==0.0.43
scikit-learn==0.23.2
scikit-optimize==0.8.1
scipy==1.5.2
sentencepiece==0.1.91
six==1.15.0
sortedcontainers==2.2.2
tblib==1.7.0
threadpoolctl==2.1.0
thrift==0.13.0
tokenizers==0.8.1
toml==0.10.1
toolz==0.11.1
tornado==6.1b1
tqdm==4.50.2
typing==3.7.4.3
typing-extensions==3.7.4.3
urllib3==1.25.10
xmltodict==0.12.0
yacs==0.1.8
zict==2.0.0
zipp==3.3.0

学習スクリプト

from mxnet import gluon
import pandas as pd
from autogluon.tabular import TabularPrediction as task
#from autogluon import TabularPrediction as task

label_column = 'X_22'

gluon.utils.download('https://archive.ics.uci.edu/ml/machine-learning-databases/horse-colic/horse-colic.data')
df = pd.read_csv('horse-colic.data', delimiter='\\s+', header=None)
df = df.loc[:,list(range(0,2))+list(range(3,23))]

df = df.add_prefix('X_')
df = df[df[label_column] != '?']

train_data = task.Dataset(df)

dir = 'agModels-predictClass'
predictor = task.fit(train_data=train_data, label=label_column, output_directory=dir)

2020年10月14日以降のバージョンでは以下の変更が必要。
before

from autogluon import TabularPrediction as task

after

from autogluon.tabular import TabularPrediction as task

推論スクリプト(テストデータを用いた評価)

from mxnet import gluon
import pandas as pd
from autogluon.tabular import TabularPrediction as task
#from autogluon import TabularPrediction as task

label_column = 'X_22'

gluon.utils.download('https://archive.ics.uci.edu/ml/machine-learning-databases/horse-colic/horse-colic.test')
df = pd.read_csv('horse-colic.test', delimiter='\\s+', header=None)
df = df.loc[:,list(range(0,2))+list(range(3,23))]

df = df.add_prefix('X_')
df = df[df[label_column] != '?']

dir = 'agModels-predictClass'
predictor = task.load(dir)
test_data = task.Dataset(df)
y_test = test_data[label_column]  
test_data_nolab = test_data.drop(labels=[label_column],axis=1)

y_pred = predictor.predict(test_data_nolab)
perf = predictor.evaluate_predictions(y_true=y_test, y_pred=y_pred, auxiliary_metrics=True)

詳細を確認するスクリプト

from autogluon.tabular import TabularPrediction as task

dir = 'agModels-predictClass'
predictor = task.load(dir)

predictor.fit_summary()['leaderboard']
#results = predictor.fit_summary()

2020年10月14日以降のバージョンでは以下の変更が必要。
before

results = predictor.fit_summary()

after

predictor.fit_summary()['leaderboard']

このように出力される。

*** End of fit() summary ***
                         model  score_val  pred_time_val  ...  stack_level  can_infer  fit_order
0      weighted_ensemble_k0_l1   0.750000       0.295788  ...            1       True         12
1           LightGBMClassifier   0.700000       0.017314  ...            0       True          8
2         LightGBMClassifierXT   0.700000       0.017695  ...            0       True          9
3           CatboostClassifier   0.700000       0.019145  ...            0       True         10
4     LightGBMClassifierCustom   0.683333       0.016992  ...            0       True         11
5          NeuralNetClassifier   0.650000       0.023902  ...            0       True          1
6   RandomForestClassifierEntr   0.650000       0.111692  ...            0       True          5
7     ExtraTreesClassifierEntr   0.650000       0.111999  ...            0       True          7
8     ExtraTreesClassifierGini   0.650000       0.112586  ...            0       True          6
9   RandomForestClassifierGini   0.583333       0.112299  ...            0       True          4
10    KNeighborsClassifierDist   0.550000       0.103969  ...            0       True          3
11    KNeighborsClassifierUnif   0.550000       0.105839  ...            0       True          2

また、「SummaryOfModels.html」というファイルが作成され中身はこのようになっている。
f:id:touch-sp:20201014213229p:plain

WSL2で最新のGluonTSを使う(2020年10月14日)

以下のコマンドでgithubから直接インストールできる。

pip install git+https://github.com/awslabs/gluon-ts.git

環境

Ubuntu 18.04(WSL2)
Python 3.7.5 

バージョン

インストールしたのは「mxnet」と「gluonts」の二つのみ。
その他は勝手についてきた。

certifi==2020.6.20
chardet==3.0.4
convertdate==2.2.2
cycler==0.10.0
gluonts @ git+https://github.com/awslabs/gluon-ts.git@91916ac9a60ce4e1bbea16110ede61c8e3bfa5fd
graphviz==0.8.4
holidays==0.10.3
idna==2.10
kiwisolver==1.2.0
korean-lunar-calendar==0.2.1
matplotlib==3.3.2
mxnet==1.7.0.post1
numpy==1.19.2
pandas==1.0.5
Pillow==7.2.0
pkg-resources==0.0.0
pydantic==1.6.1
PyMeeus==0.3.7
pyparsing==2.4.7
python-dateutil==2.8.1
pytz==2020.1
requests==2.24.0
six==1.15.0
tqdm==4.50.2
ujson==1.35
urllib3==1.25.10

Pythonスクリプト

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

from mxnet import gluon
import zipfile

gluon.utils.download('https://archive.ics.uci.edu/ml/machine-learning-databases/00275/Bike-Sharing-Dataset.zip')

with zipfile.ZipFile('Bike-Sharing-Dataset.zip') as existing_zip:
    existing_zip.extractall('.')

def one_hot(x, start_zero = True):

    if not start_zero:
        x = x-1 
    category_n = x.max() + 1
    one_hot_vec = np.identity(category_n)[x]

    return one_hot_vec.transpose(1,0)

df = pd.read_csv('day.csv',index_col=1)

feat1 = np.array(df.hum).reshape((1,-1))
feat2 = np.array(df.temp).reshape((1,-1))
feat3 = np.array(df.windspeed).reshape((1,-1))

feat4 = one_hot(df.weekday, start_zero=True)
feat5 = np.array(df.workingday).reshape((1,-1))
feat6 = one_hot(df.weathersit, start_zero=False)
feat7 = one_hot(df.season, start_zero=False)

features_real = np.concatenate([feat1, feat2, feat3, feat4, feat5, feat6, feat7], axis=0)

from gluonts.dataset.common import ListDataset

training_data = ListDataset(
    [{"start": df.index[0], 
        "target": df.cnt[:-14],
        "feat_dynamic_real": features_real[:,:-14],
        #"feat_dynamic_cat": features_cat[:,:-14]
        }],
    freq = "1D")

test_data = ListDataset(
    [{"start": df.index[0], 
        "target": df.cnt,
        'feat_dynamic_real': features_real,
        #"feat_dynamic_cat": features_cat
        }],
    freq = "1D")

from gluonts.model.deepar import DeepAREstimator
from gluonts.mx.trainer import Trainer
#from gluonts.trainer import Trainer

estimator = DeepAREstimator(freq="1D", 
                            prediction_length=14, 
                            context_length=28,
                            use_feat_dynamic_real = True,
                            trainer=Trainer(epochs=50))
predictor = estimator.train(training_data=training_data)

from gluonts.evaluation.backtest import make_evaluation_predictions
from gluonts.dataset.util import to_pandas

forecast_it, ts_it = make_evaluation_predictions(
    dataset=test_data,  # test dataset
    predictor=predictor,  # predictor
    num_samples=100,  # number of sample paths we want for evaluation
)

plot_length = 30
prediction_intervals = (50.0, 90.0)
legend = ["observations", "median prediction"] + [f"{k}% prediction interval" for k in prediction_intervals][::-1]

fig = plt.figure()

for x, y in zip(test_data, forecast_it):
    to_pandas(x)[-plot_length:].plot()
    y.plot(color='g', prediction_intervals=prediction_intervals)
    plt.grid(which='both')
    plt.legend(legend, loc='upper left')

fig.savefig('result.png')


以下の変更が必要。(今のところ変更しなくても警告が出るだけで実行はできる)
before

from gluonts.trainer import Trainer

after

from gluonts.mx.trainer import Trainer

変更しなかった場合の警告

DeprecationWarning: gluonts.trainer is deprecated. Use gluonts.mx.trainer instead.
  from gluonts.trainer import Trainer

結果

今のところ自分の環境ではWSL2でグラフを描画することができない。
そのため画像として保存している。
f:id:touch-sp:20201014162243p:plain