atma-inc__blog

atma株式会社の公式ブログです。

主催者から見た #atmaCup 5 は、データ分析のお祭りのようなコンペでした。

atma株式会社で取締役(あとデータサイエンティストとかエンジニアとかもろもろ)をしています、山口(@nyker_goto)です。 先日 5/29 ~ 6/6 にかけて atmaCup#5 を開催しました。コンペの雰囲気は twitterhashtag #atmaCup や参加者さんの素敵な参加レポなど見ていただけると雰囲気を感じてもらえると思っています。

upura.hatenablog.com takaito0423.hatenablog.com agtn.hatenablog.com amalog.hateblo.jp nonbiri-tereka.hatenablog.com

公式の振り返りは atmaCup#5 開催レポート #atmaCup - atma-inc__blog こちらから。

このエントリでは運営とコンペ設計にかかわらせてもらっている nyker_goto の立場から今回のコンペ atmaCup#5 について振り返っていきたいと思います(データについては秘密保持の関係上細かいことは述べられないので、ふわっとした記述になることご容赦ください)。

atmaCup とは

そもそも atmaCup は atma 株式会社が主催する、オンサイトデータコンペです。

オンサイトデータコンペとは 実際に会場に集まり、準備されたデータをテーマに沿って分析・予測を行い、 その精度を競うイベントです。全員で一斉にスタートし、短い時間で決着するため 参加者のスキルがオンラインのデータコンペより強く結果に表れます。 実際に顔を合わせて分析を行うため、コンペ終了後に上位者に直接質問できることも特徴です。 atmaCup用コンペサイトぐるぐる https://www.guruguru.ml/ より引用

Kaggle など他のコンペティションでは期間が数カ月ですが atmaCup では比較的時間が短い (1日あるいは一週間) ため、素早い分析と実装能力が求められます。また通常は実際に同じ場所に集まって分析をしてもらうので、終わったあとに参加者同士で集まって解法について話をできるのも特徴です。

今回はじめてだったことが2つあります。1つがテーマが学術業界からの出題であったこと、もうひとつが完全オンライン開催であったことです。

初の学術業界からの出題

今回はじめて学術系テーマからの出題でした。若干大げさですが、産学連携なコンペティションだったと言えるかもしれません。

僕自信、データ分析は実際に応用し価値を出してこそはじめて意味があると思っています。その文脈で言えば、今回の課題は学術的・ひいては社会的にインパクトを与えられる課題でしたから、これを多数のデータ分析者が解いて良いモデルや予測のための知見が貯まることは非常に価値が高いことですし、その場を atmaCup が提供できたことをとてもうれしく思っています。

今後もっとこのような機会が広がってくれれば良いなと感じていますし、もちろん atmaCup でも引き続き取り組んでいきたいと思っています。とはいえ課題もあり、産の側からするとどうしても予算の問題が出てきてしまう (今回は手弁当的に開催となりましたが、持続可能性を考えるとずっとそうしてられないのも事実なのが世知辛いところです。特に弊社のような小さい会社だと特に。) ので、このあたりうまく解決できるスキームができれば良いなと模索しているところです。

初の完全オンライン開催

今回はコロナウィルスの影響もあり、完全オンラインでの開催となりました。過去開催ではオンライン推奨(atmaCup#4)はあったものの、基本的にはオフライン会場をメインとして進行していました。実際僕も会場に行って司会進行など行っていました。

atmaCupは時間と場所が決められているコンペで、それゆえに終わったあとの懇親会で参加者同士で実際に何をやったかを熱量(と記憶)があるうちに対面で議論できるのが面白いところの一つだと考えています。 これは自分が kaggle days tokyo に参加したときの経験やその他オンサイトのコンペに参加した方からのヒアリングでも楽しい要素の一つとして挙げられることが多い項目です。ゆえに今回完全オンラインになり、オフラインだったからよかった要素が完全オンラインでなくなってしまわないかという不安がありつつの開催でした。

f:id:atma_inc:20200616170637p:plain
300人を超える方に応募いただきました!!(漏れてしまった方ごめんなさい…)

しかしこれは良い意味で裏切られました。告知の段階で予想を大きく上回る300人以上の応募をいただき、またデータの不備でご迷惑をお掛けしたのにもかかわらず、開催期間中のディスカッション上での活発な議論や twitter での緑の画面報告*1など、客観的に見ても過去最大規模に盛り上がったコンペだったと思います。

とくに twitter でのハッシュタグ投稿がとても活発で、過去最もお祭りな感じがあったコンペでした。 これは運営だけでは作れない空気感であって、ひとえに参加者さんのコンペを楽しもうという思いによって作られたものだなと感じています。本当にありがとうございました。

🎉 ディスカッションの盛り上がり

f:id:atma_inc:20200616185239p:plain
最終的にディスカッションの数は最多の78個になり、最多いいねはなんと100を超えました🎉 / 情報はシェアしようという気持ちが伝わってきてとても素敵だなと思います。

ぐるぐるには Kaggle などと同じくディスカッションの機能があります。ディスカッションとは参加者さん同士でコンペのデータや分析手法などについて議論する掲示板のようなものです。今回合計で 78 件のディスカッションが作成され過去最大となりました。 データの機密性ゆえ参加者さん以外にお見せできないのが残念ですが、基礎的な分析をするコードを共有したものから、運営が唸るような波形に関するドメイン知識を満載に盛り込んだディスカッションなどなどバラエティに富んでいて僕も毎日新しいディスカッションを見るのが楽しみでした。

🎉 過去最大の Submission 数

(残り時間19時間での tweet。 このとき3000submitを超えて喜んでいるがここから更に1600増える。)

submission 数は 4000 超 (#4602)でした。初回の total submission が 114 だったのを考えると遠くに来たものだなあと考えさせられます。

特に最終日の submission 数は本当に多くて、ぐるぐるの AWS Metrics を見るとその様子が露骨にわかって面白いです。submission 〆切後にはしばらくAPIがダウンするという事態も発生してしまいました、申し訳ありませんmm どうやら僕の設定が悪くてスケーリングが間に合わなかったようです。

f:id:atma_inc:20200616170539p:plain
最終日にいくに連れて増えていくCPU使用率

submission はちゃんと課題に取り組まないと増えないゆえ、課題に取り組むモチベーションが最も表れる数字であると考えています。 コンペ中の僕の目標の一つは submission を増やすことで、特に 0 submit のコンペに慣れていない方が submit できるためにはどうしたら良いかなーということを考えつつチュートリアルを作っていたりします。

今回は完全オンラインで、オフラインに比べるとコミットメントがゆるいにもかかわらず 188 / 218 (85%以上! 100% まであとちょっと👊) のチームに submit していただきました。また特に上位チームを始めとしてチームの submit 上限まで submit しているチームも多く参加者さんの熱い思いを感じられました。ありがとうございました!!

🤔 今後のオンライン対応について

完全オンラインになったことで、大阪・東京から離れたところに住んでいる方も参加できるようになり (なんとアメリカからも!参加いただきました)、多数の応募をいただけたことで盛り上がりが加速したようにも感じていて、オンライン開催は悪いところばかりではないなと重う次第です。

とはいえ閉会式などしているとやはり最後の懇親会ぐらいはやりたいなー(もうちょっと気軽に解法を共有できる場がほしい)とも思えたので、次回以降はリアルとオンラインのバランスを考えつつ決めていければなと思っています。

個人的感想: ぐるぐるのリアルタイム更新について

ぐるぐるは atmaCupではコンペ用のサイト(submissionなどを行なうシステムを含んだウェブアプリケーション) のことです。このサイトは主に僕が開発を担当しています。今回開催に先立っていろいろと準備が間に合わなかったこともあり、ひじょーによろしくないのですが開催途中にコンペサイトの更新をバンバンやっていました(もちろん検証環境でテストなどやったうえでではありますが、よくはない)。普通ならお叱りを受けるところだと思うのですが特に苦情もなくみなさん優しくてありがとうという気持ちです……

あと更新した部分を twitter などで報告してくれるのも開発者としてとてもモチベーションが上がりました、本当にありがとうございますmm 今後も使って楽しいシステムになるように更新していきますのでよろしくお願いいたします😆 こうしてほしいなど意見があれば気軽に twitter で @nyker_goto 宛に reply をいただければ、爆速で実装したいと思います。

最後に

次回 atmaCup#6 も企画中です!! 参加したことある方は是非リピート参加を、気になってるけど出たことない〜という人も気軽に参加してください! ;) (次回もデータ分析経験がない方でも submission まで出来るようなチュートリアルを行う予定です。分析コンペに出たことがない・分析経験があまりない方も気軽に参加いただけると嬉しいです。)

募集は connpass の atmaのページatma twitter アカウント で告知をする予定です。良ければフォローしてください!
(僕のアカウント @nyker_goto も事実上会社の告知アカウントなのでこっちでも大丈夫です)

おまけ

コンペ中、チーム名大喜利が自然発生していたのですが、どのチームもセンスがあってとても楽しかったです! こういうのもなんだかお祭りな感じがあって素敵ですね。

*1:スコアアップした時だけに現れる画面のことです。中毒性があるとの噂

atmaCup#5 開催レポート #atmaCup

はじめに

atmaCup、今回で6回目となりました。
今回はコロナウイルスの影響もあり初の完全オンライン参加での開催となりました。

参加者のレポ

参加者側の感想などはこちらをチェックください。

upura.hatenablog.com takaito0423.hatenablog.com agtn.hatenablog.com amalog.hateblo.jp nonbiri-tereka.hatenablog.com

いつもたくさんのレポありがとうございます。

開会式

開会式

今回新たに導入されたルール

チームコスト

チームの平等性を高めるべく、今回新たにチームマージ時にコストが必要となりました。またチームマージするとsubmission上限が増える特殊?ルールも追加されています。

f:id:atma_inc:20200610204513p:plain

なお、AutoMLはコスト100なので誰ともマージできません😢

f:id:atma_inc:20200610204552p:plain

序盤

初心者向け講座 #1

今回もデータコンペに参加したことがない・分析ははじめてという方向けに nyk510 から初心者向け講座を開催しました。

初心者向け講座#1は主にデータの見方についての講座で pandas profiling を使った可視化や波形データを t-SNE による低次元射影可視化して特徴を探る方法などを紹介しました。

初心者向け講座 #2

初心者向け講座 #2は実際にSubmissionするまでの講座でした。 LightGBMによる基本的なモデルの作り方 + 波形ならではの特徴を木構造にどう盛り込んでいくか、に関して深堀して解説しました。

データ不備について

中盤

すでに上位陣は0.9を超えるスコアで争っています。

終盤

上位20位まで0.9超え、1位は0.93超えてます。
しかもKaggleRankはContributor以下のチームです。

閉会式

まとめ

f:id:atma_inc:20200616181610p:plain 途中データの不備があり、お手数をおかけしましたが、無事に終了することができました。
特に今回オンラインということもあってかdiscussionが大いに盛り上がりました。 ひとえに皆様のご協力があってatmaCupは成り立っております。

Next?

次回の開催も決定しております!!(日程は未定) 開催決まり次第Twitter等で告知させていただきます。

DRFとNuxtを使って画像分類(機械学習)をする①

はじめに

インターンしている小林です.この記事では,DRFDjangoのいい感じのフレームワーク)を使って,APIを作るまで行います.記事は二編構成とし,一編はDRFによるAPI作成,二編はNuxtを用いてユーザが実際に入力することを想定してフロント作成します.具体的には,PyTorchのresnetを用いて,入力フォームから受け付けられた画像を推論して上位10位までの結果を表示させます.一編では,詳細な機械学習アルゴリズムは説明せずに,APIを作る工程に重きを向けます.読者の対象はDRFを初めたての人が対象であり,機械学習の画像処理をある程度把握している人が対象となります.

構築したAPIは以下のような感じになります.

f:id:atma_inc:20200327163520g:plain
今回作るAPI画面

結果で返しているのはresnet-18に入力した画像を推論させ,確率値が高い上位10個を表示させています.用いてるモデルはImageNetの学習済みモデルです.

DRFについて

Django」は Python で Webアプリケーションを作成するためフレームワークですが、「Django REST Framework」という Django のためのパッケージを使うことで、RESTful な API バックエンドを簡単に構築することができます。実際の現場では、SPA(シングルページアプリケーション)やスマホアプリのバックエンドとしてよく利用されています(引用:現場で使える Django REST Framework の教科書 (Django の教科書シリーズ))。とのことですが,基本的にはDjangoで足りない所を補ってやりたいというのが,これを使っている理由です.ただし,色々な機能があるため少し重たいファイルであることはデメリットですが,それを超える良い機能が複数あるので慣れると使いやすいものかと思われます.

目次

  1. 環境構築
  2. DRFのモジュール作成
  3. resnetの作成
  4. APIを使って推論
  5. まとめ
  6. 次回

環境構築

環境構築は以下を基本として構築しました.

https://qiita.com/michio-k/items/371881a6b8ecfa768606

ファイル構成は以下のようになります.

home
|- backend
| |- core(Djangoのプロジェクトが入る)
| |- app(APIを作成)
| |- Dockerfile
| |- requirements.txt
|- front
| |- nuxt (フロントのプロジェクト)
| |- Dockerfile
|- .gitignore
|- docker-compose.yml
|- README.md

今回操作するのは上記のbackendの方となります.二編目でfrontの方をいじっていきます.画像のAPIを作成する上で,いくつかインストールする必要があるモジュールがあるので,記していきます.

FROM python:3.7
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
RUN apt-get update && apt-get install -y \
    libblas-dev \
    liblapack-dev\
    libatlas-base-dev \
    libsm6 \
    libxext6 \
    libxrender-dev

ADD requirements.txt /code/
RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt

インストールするpythonモジュールです.ここでは,django-jsonfieldを使って辞書のデータを受け付けるようにします.実務的になると,PostgresやMySQLを使う方がいいと思われるので,そちらをデータベースとして参照する方が望ましいです.今回は簡易的なものなので,これを使わず,sqlite(デフォルトの設定)でやっていきます.これらのことは後に後述します.

* 補足として,DjangoでPostgresやMySQLなどでデータベースを使用したいときは,以下のサイトを参考にしてください. qiita.com

*

画像分類の処理では今回はPyTorchを使っていきます.pillowはDRFDjango)が画像を読み込む時に必要となるのでインストールしておきます.

# backend/requirements.txt
#Django
django
djangorestframework
django-filter
django-cors-headers
django-jsonfield
#Extra
numpy
pillow
opencv-python
torch
torchvision

次にdocker-compose.ymlを記述します.以下の通りになります.

version: '3'

services:

  #front:
    #container_name: front
    #build: ./front
    #tty: true
    #ports:
     # - '3000:3000'
    #volumes:
      #- ./front/:/usr/src/app
    # command: [sh, -c, "cd nuxt/ && npm run dev"]

  backend:
    container_name: backend
    build: ./backend
    tty: true
    ports:
      - '8000:8000'
    volumes:
      - ./backend:/code
    # command: python manage.py runserver 0.0.0.0:8000

これにて環境構築が終わりです.補足ですが,Dockerを使わなくてもrequirements.txtに記述してあるモジュールをインストールしている環境であるならば,これからやることはできます.また,下のコードで先程構築した環境に入ることができます.

docker-compose build #Dockerfileの環境を立ち上げる
docker-compose up -d #起動
docker exec -it backend bash #containerの中に入る

DRFのモジュール作成

DRFのプロジェクトとアプリの作成

これからDjangoのプロジェクトを作成していきます.この方法は通常のDjangoのやり方と変わりません.

django-admin startproject core .
python manage.py startapp app

にてファイルを作成します.次にcore内のsettings.pyに今回作ったファイルとDRFを読み込ませます.また,各種必要なものを記述しておきます.MEDIAは画像の保存先を指定するために必要となりますので追記してください.

#python:core/settings.py
ALLOWED_HOSTS = ["localhost"]

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "rest_framework", #add
    "app", #add
]

#add
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR,"media")

resnetのファイル

この記事での推論はresnetを使用します.また,簡易的なものであるため,自前の学習済みモデルを使用せずネット上に公開されているFinetuned-modelを利用します(ImageNetです).そのため,以下にしているファイルを事前にダウンロードしてください.

以上で初期に用意するファイル一式の準備はできました.上記のファイルとresnet用に用意するファイルは以下のように作成してください.

backend
├── Dockerfile
├── app
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── resnet #add
│   │   ├── config
│   │   │   ├── imagenet_class_index.json
│   │   │   └── resnet18-5c106cde.pth
│   │   ├── model.py
│   │   └── predict.py
│   ├── tests.py
│   └── views.py
├── core
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── requirements.txt

上記のようなファイル構成になっていると大丈夫です!

modelsの作成

今回APIとして必要になるのは以下の通りです.

  • 入力:入力した画像の名前と入力する画像
  • 出力:確率値が高い上位10までのラベル一覧と確率値

となります.そのため受け付けるフィールドは三つとなります.

#python:app/models.py
from django.db import models
import jsonfield
# from django.contrib.postgres.fields import JSONField  Jsonを受け付ける

class ImageModel(models.Model):
    name = models.CharField(max_length = 128,null=True,default="unknown")
    image = models.ImageField(upload_to="media")
    predict = jsonfield.JSONField()

ここではjsonfieldというモジュールを使って,出力するための値を受け付けます.出力する値はjson形式にしたいのですが,Django内で提供されているJSONFieldはデータベースがpostgresやMySQLなどに対応しており,設定を変更しなければ使用できません.これはDjangoのデフォルトのデータベースがsqliteであり,対応していないためエラーが起こります.この問題を解決するために,今回は設定を省略し,jsonfieldというもので簡単にsqliteが受け付けられるようにしました.

また,今回作成したデータベースを登録するためにadminの内容を変更します.以下のように記述してください.

#app/admin.py
from django.contrib import admin
from .models import ImageModel

@admin.register(ImageModel)
class ImageModel(admin.ModelAdmin):
    pass

serializerの作成

続いてserializerの作成です.serializerはDRF特有のものであり,通常のDjangoにはありません.詳細は記事は以下のものが参考になるかと思いますので,乗せて起きます.

やっていることは,入力されたデータの値がModelの中身で定義した型と一緒なのか?ということをやったり,Json形式で入力されたものをPythonで読み込めるようにしたりとそんなことをやっています.

appの直下にserializers.pyのファイルを作成し,以下のように記述します.

#app/serializers.py
from rest_framework import serializers
from .models import ImageModel

class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = ImageModel
        fields = ("id","name","image","predict")
        read_only_fields = ('predict',"id")

上記のように今回は書きました.入力としてはnameとimageのみなので,入力に必要ないものは外しています.また,上記のMetaに関する情報はhttps://teratail.com/questions/87695 が参考になるかと思いますので適宜参考にしてみてください.

viewsの作成

今回はDRFのビューはクラスベースビューを用いて,ModelViewSetを使って見ました.また,postを受け付けるコードをactionで対応するようにしました(これはdef post()メソッドを用いてもらっても大丈夫です).

#app/views.py
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework import status
from .models import ImageModel
from .serializers import ImageSerializer
from .resnet.predict import predict #resnetの予測

class ImageViewSet(viewsets.ModelViewSet):
    queryset = ImageModel.objects.all() 
    serializer_class = ImageSerializer #check ユーザのどんなクエリを受け付けるか

    @action(detail=False,methods=["post"])
    def classification(self,request):
        serializer = self.serializer_class(data = request.data)
        serializer.is_valid(raise_exception=True)
        img = request.data["image"]
        name = request.data["name"]
        res = predict(img)
        # 保存
        item = ImageModel(name=name, image=img,predict = res)
        item.save()
        return Response(res, status=status.HTTP_200_OK)
  

serializer_classで受け付ける入力フォームを決めています.

@action(detail=False,methods=["post"])
def classification(self,request):
    serializer = self.serializer_class(data = request.data)
    serializer.is_valid(raise_exception=True)
    img = request.data["image"]
    name = request.data["name"]
    res = predict(img)
    # 保存
    item = ImageModel(name=name, image=img,predict = res)
    item.save()
    return Response(res, status=status.HTTP_200_OK)
    

serializer = self.serializer_class(data = request.data)は入力されたデータが正しいかどうかを検証するためにいれています.極端な話,PDFのファイルが入力された時,エラーを出力してくれます.serializer.is_valid(raise_exception=True)を記述するとこの時点でエラーのデータがあるとエラーの文章で値が返されます.また,記述方法として,

if serializer.is_valid():
    img = request.data["image"]
    name = request.data["name"]
    return Response(serializer.data,status=status.HTTP_200_OK)
else:
    return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)

があります.これは,if文章でTrueかFalseを処理して中身を実行するかどうかを判断しています.しかし,この書き方は若干情緒でもあるのでこれを省略しserializer.is_valid(raise_exception=True)だけを記述することで,上記のことと全く同じようにしてくれます.

ifの中身は,predict(img)でdictの結果を返してresで受け付けています.このresは先ほどの確率値の上位10個が入っている値の一覧が格納されています.これらのデータをItemModel()に入れ,保存しています.

urlの繋ぎこみ

繋ぎこみをします.app以下にurls.pyのファイルを作成して以下のように記述してください.

#app/urls.py
from rest_framework import routers
from .views import ImageViewSet

router = routers.DefaultRouter()
router.register(r"^image",ImageViewSet)
#core/urls.py
from django.contrib import admin
from django.urls import path,include
from django.conf import settings
from django.conf.urls.static import static
from app.urls import router as router
urlpatterns = [
    path('admin/', admin.site.urls),
    path("",include(router.urls))
]
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL,document_root = settings.MEDIA_ROOT)

繋ぎこみの際にファイル名を注意してください.これでlocalhost:8000/image/classificationとURLを入れた時に,APIをPOSTできるようになります.

これでDRFで記述すべきことは終わりました.次に,resnetの方を記述していきます.

resnetの作成

resnetのディレクトリの中のmodel.pyを作成します.以下のように記述してください.

#app/resnet/model.py
import torch
from torchvision import models

def resnet_model():
    MODEL_PATH = "./app/resnet/config/resnet18-5c106cde.pth"
    model = models.resnet18(pretrained=False)
    model.load_state_dict(torch.load(MODEL_PATH))
    model.eval()
    return model

今回は簡易的に作っているため,MODEL_PATHをこんな風にPathを書くことはナンセンスだと思うので注意してください(笑).また,model.eval()を忘れないでください(これを書くの忘れて何時間も悩んだのは裏の話).

次にpredict.pyを作成します.以下のように記述してください.

#app/resnet/predict.py
from PIL import Image
import json
import cv2
import numpy as np
import torch
import torch.nn as nn
from torchvision import transforms
from .model import resnet_model

preprocess = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
softmax = torch.nn.Softmax(dim=1)
model = resnet_model()
def predict(img):
    # json
    with open("./app/resnet/config/imagenet_class_index.json", 'r') as f:
        image_dict = json.load(f)
    # img
    img = Image.open(img)
    img = img.convert('RGB')
    img = np.array(img)
    img = preprocess(transforms.ToPILImage()(img)).unsqueeze(0)

    # prediction
    predict = model(img).data
    prob = softmax(predict)[0].tolist()
    best_ten = np.argsort(prob)[::-1][:10]

    response = []
    for i,rank in enumerate(best_ten,1):
        label = image_dict[str(rank)][1]
        response.append({"rank":i,"prob":prob[rank],"label":label})

    return response

入力された画像はImage.open(img)にて読み込みます.cv.imread(img)で読み込むことはできないので注意してください.また,

preprocess = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

この部分はImageNetの学習方法と同じようにしているので,これがなければ出力で欲しいラベルが返って来なくなります pytorch.org

returnで返しているのは配列であり,中身は辞書型になっています.probの確率値はtolist()でリスト型にしています.これはデータベースにデータが保存されるときに,データの型がnumpyであるとエラーの原因になるためです(これはちょっとはまり所でした).そこで,tolist()で通常の数値型に変更し,エラーの原因を未然に防ぎます.参照したのは以下のページです.

stackoverflow.com

APIを使って推論

ここまででAPIは作れたので,実際の画面で確認していきます.

python manage.py makemigrations
python manage.py migrate

をしてください.これはDjangoの定型文みたいなものなので,そうなんだーみたいな感じでやってください(これはデータベースを作ってくれたりしている).また,Modelsの中身を変更したり,追加する場合は上記のコードをもう一度入力してください.すると,更新されます.

python manage.py runserver 0.0.0.0:8000

を実行しlocalhost:8000/imageのurlを検索すると以下のような画面が出てくると思います.

f:id:atma_inc:20200327171142p:plain
APIの画面

ここに保存されたデータの一覧が出力されるようになります.localhost:8000/image/classificationのurlを検索するとAPIをPOSTできる画面が出力されるのでやって見てください.

まとめ

今回は画像を入力として受け付けて,上位10個の確率値とラベルを出力するAPIを作成しました.モデルはresnetを使用し,ImageNetの学習済みモデルで値を出力させました.これらは適宜自分のモデルの差し替えが可能なので他のものでも試していきたいと思います.

次回

次はNuxtを利用して,front側を作成していきたいと思います.入力フォームを作成し,実際にユーザが画像を投稿するようなイメージで作成します.その画像をAPIに投げ,値が返ってくるところまでを実装し,画面に表示させます.

atmaCup#4 二転三転

はじめに

atmaCupでカメラマン兼バリスタ兼運営をやっている@yukiです。


f:id:atma_inc:20200318122908j:plainf:id:atma_inc:20200125102639j:plain
いつもはコーヒーの人をしています。
今回はコロナのため実施しませんでした><

弊社のイベンター@TanakaMidnightさんが一通り報告記事

atma.hatenablog.com

を書いてくれたので私が何でこんなブログを書いているのか自分でも若干解らないでいますが、違う観点から報告記事をということだったので少しカジュアル目に裏話的なことも入れてお届けしてまいります。

初の1週間開催

今回は3月1日~8日と初の1週間使ってのatmaCupでした。
参加された方はなぜ1週間だったのかなんとなく感じられていると思いますが主に2点あります。

  • 解析データのボリューム

今回のコンペは解析のお題のボリュームがかなりありました。 多くの協賛企業にお力をお借りし、課題設定をしていくうちに1日で解析するのは少し厳しいデータ量となっていきました。

データの機密保持の懸念と、要求されるマシンスペックが主な理由で、スポンサーの日本マイクロソフト株式会社様から分析環境をご提供いただきました。

ただ、慣れない環境で解析をしていただくことになると、やはり今まで通り1日開催だと全員が競い合うところまでの時間が足りない可能性がありました。

f:id:atma_inc:20200318150733j:plain
Azureと解析データを持つ参加者の図

コンペの満足度の観点としても今回は期間を広く持ったほうが良さそうということで初日と最終日を会場に集まっていただき、間の期間も解析していただけるようなコンペを開催することになりました。

急ピッチの準備

今回は前回開催から約1ヶ月弱しか間隔がない中での開催でした。 
これがかなり運営的には大変なポイントでした。

  • #0から#1 (5月上旬 - 8月上旬) 3ヶ月
  • #1から#2 (8月上旬 - 11月下旬) 4ヶ月弱

  • #2から#3 (11月下旬 - 1月下旬) 2ヶ月弱

    f:id:atma_inc:20200318132154j:plain
    準備期間 ※画像はイメージです

いつもだと開催1ヶ月前ぐらいに開催告知を実施するような形がメインだったのですが、
今回は準備だなんだかんだと時間がかかり、開催の半月前公開になりました。
遠方からご参加の方には大変ご迷惑をおかけしました。orz



また公開直前に大阪会場開催が決定しました。 
今回はもともと東京会場のみでの開催予定でしたが、前回の#3で2会場同時開催が大きな問題なく実施できたことや、参加者の方からの反響も大きかったことから、急遽大阪にも会場を持つことが決定しました。

#1でもご協力いただいた、OCA大阪デザイン&IT専門学校様のおかげで、なんとか当日の会場も確保し、無事情報公開することができました。


OCA大阪デザイン&IT専門学校様、本当にありがとうございました。

コロナ襲来

やっとの思いで無事公開・開催告知をしたのですが、ここでアイツがやってきます。


そう皆さんも頭を悩まされているであろうコロナです。
展示会などの大きなイベントが中止や延期になっている中、今回のatmaCupの規模感(会場の来場予定者:50名程度)ではなんとか開催できるかと思っておりましたが、atmaCup直前の28日全国の小中高へ臨時休校要請が出されました。

この状況で本当に開催して大丈夫なのか、様々な議論を直前に実施した末、オンライン参加を推奨する形で
開催するというかなりいつもと雰囲気の違うatmaCupとなりました。



私は1日目の大阪会場でスタッフをしていたのですが、想定していた以上にオフライン参加の方は少なく、ほっとするやら悲しいやら複雑な気分でした。
データコンペ自体はこのような中でも多くの方にご参加いただき、
盛り上がっていたようで、よかったです。


ご参加いただいた皆様本当にありがとうございました。

メダルについて

今回のメダルも前回のコンペと同様 共同開催をさせていただきましたリテールAI研究会様のアイコン入りの特別製を作成しております。
またアイコン以外にも特別製の箇所がございます。入賞者の方はぜひ見つけてみてください。※発送は3月下旬ごろを予定しております。

f:id:atma_inc:20200318160821j:plain
#3の特製デザインのメダル
サイバーエージェントAI事業本部様のロゴ入り

長くなりましたが、ドタバタとトラブルに見舞われながらも、なんとか開催できた#4の開催記録でした。

Next atmaCup

次回の準備も着々と進めております。
情報開示に乞うご期待!

いつも
突然の情報公開でご迷惑をかけているというお声も伺いましたので、次回開催時は申し込み受付開始前に告知を実施した後のイベントページ公開となる予定です。


@nyk510のアカウントを通知していただいている方もいらっしゃるとのことでしたが、そこまでしなくても公平に申し込めるよう準備の方進めさせていただきます。


今後ともよろしくお願いします。

atmaCup#4 開催レポート #atmaCup

はじめに

atmaCup、今回で5回目となりました。
今回は前回と同様、東京と大阪同時開催に加え、
コロナウイルスの影響もあり初のオンライン参加可での開催となりました。

参加者のレポ

参加者側の感想などはこちらをチェックください。

kabayan55.hatenablog.com nonbiri-tereka.hatenablog.com py2k4.hatenablog.com 皆様、レポありがとうございます。

一般社団法人リテールAI研究会様 との共同開催

今回は一般社団法人リテールAI研究会様 との共同開催となりました。
さらに、分析環境としてAzureを無償で提供いただきました!!

retail-ai.or.jp

協賛企業様(順不同・敬称略)

f:id:atma_inc:20200308124220j:plain コンペ中のお菓子、ドリンクなど差し入れをいただきました、ありがとうございます!!

初日

東京会場は前回と引き続きサイバーエージェント様の会場をお借りしました。 cyberagent.ai

大阪会場はOCA大阪デザイン&IT専門学校様の会場をお借りしました。 www.oca.ac.jp

f:id:atma_inc:20200312173731j:plain

f:id:atma_inc:20200301122046j:plain スタッフはマスクをして参加。

Zoom & YoutubeLiveで配信

今回、東京会場、大阪会場、オンライン参加の3パターンの参加方法がありましたので、
東京と大阪の会場間はZoomで、
オフライン参加の方はその模様をYoutubeLiveで視聴という形となりました。 f:id:atma_inc:20200312172915p:plain (イメージ図)

f:id:atma_inc:20200313195400p:plain

2~6日目

2~6日は各自オンラインで分析を行いました。

初心者向け講座

初心者向け講座もZoomで配信。 f:id:atma_inc:20200313195657p:plain

最終日

最終日は東京会場、大阪会場で終日集まって分析。(オンライン参加の方もOK!)

朝一でnyk510のベースライン更新。 f:id:atma_inc:20200313200254p:plain いきなりの5位!!

大阪会場ではどよめきが(笑)

f:id:atma_inc:20200308111026j:plain ラストスパートで皆さん集中されています!!

f:id:atma_inc:20200308124220j:plain 最終日も協賛企業様から差し入れが、ありがとうございます!!

結果発表

結果は、
1位: paoさん
2位: hmdhmdさん
3位: nekoumeiさん
でした!

1位のpaoさんは前々回の優勝者、圧巻の優勝でした!!

まとめ

f:id:atma_inc:20200308185913j:plain 東京会場 f:id:atma_inc:20200308183419j:plain 大阪会場

今回はコロナウイルスの影響もあり、急遽オンライン開催や会場変更などあり参加者の方々にはご不便、ご面倒をおかけしました。
次回以降はより一層運営に力を入れ、ストレス無く開催できるように努力いたしますので、ご助力いただければ幸いです。

Next atmaCup

もちろん次回も開催予定があります!!

近日中に何らかの発表させていただけると思いますので、Twiiterやconnpassのページをチェックして楽しみにお待ち下さい!!

atma.connpass.com

twitter.com

atmaCup#3 CA×atmaCup開催レポート #atmaCup #CA_atmaCup

はじめに

レポートの公開までお時間がかかってしまい申し訳ありませんでした。

2020/1/25(土)にサイバーエージェントAI事業部様とコラボして開催させていただいたatmaCup#3 CA×atmaCupが無事終了しました。

本記事では、当日の模様など写真を元にまとめてみました。
f:id:atma_inc:20200308145018j:plain

atmaCup初の東京大阪の2会場同時開催となり、定員を大きく上回る130人以上にお申し込みをいただきました。
当日は、大阪会場37名 東京会場60名にご参加いただき、
Kaggle Grand Master3名を筆頭に半数以上がKaggle Expert以上というKaggleでも成績を残されている実力者ぞろいの大会となりました。

会場の様子(東京)

東京会場は、サイバーエージェント様の渋谷スクランブルスクエアにて行いました。 f:id:atma_inc:20200125182748j:plain

会場の様子(大阪)

大阪会場は、梅田の会議室で行いました。 f:id:atma_inc:20200125121522j:plain

開会

弊社nyk_gotoよりオープニング。 f:id:atma_inc:20200125182333j:plain

f:id:atma_inc:20200125121523j:plain

本日のデータの説明などが行われました。

コンペ開始

コンペが開始されました。 皆さん集中して解析しています。 f:id:atma_inc:20200125121105j:plain f:id:atma_inc:20200125121135j:plain

こだわりのコーヒー提供

今回も、弊社の社員兼カメラマン兼バリスタの下村がハンドドリップ、フレンチプレス、エアロプレスなどさまざまな方法で抽出したコーヒーを提供しました。 f:id:atma_inc:20200125102638j:plain

初心者向け講座vol.1 はじめてのSubmission

弊社nyk_gotoより初心者向け講座の1回目が行われました。 f:id:atma_inc:20200125121222j:plain

お昼ごはん

Submissionして一段落ついた方からお昼ごはんタイム f:id:atma_inc:20200125124239j:plain

初心者向け講座vol.2

弊社nyk_gotoより初心者向け講座の2回目が行われました。 f:id:atma_inc:20200125121127j:plain

大詰め

デッドラインまであと1時間、チームマージも締め切り皆さんの集中力もピークに。 f:id:atma_inc:20200125182405j:plain

コンペ終了

18:00にコンペが終了しました。 みなさん緊張から開放されゆったりされていました。 f:id:atma_inc:20200125182909j:plain 終了時点のPublicでは、
1位: 完全に理解したチーム(hakubishinさん & takuokoさん)
2位:e-toppoさん
3位: Jackさん
でした。

f:id:atma_inc:20200308151933p:plain

結果発表

今回は東京と大阪同時開催の為、東京と大阪を中継で繋ぎ発表しました! f:id:atma_inc:20200125183056j:plain

結果はなんと!!
1位: Publicから、+1shake upしたe-toppoさん。
2位: Publicから、+3shake upしたnyanpさん。
3位: Publicから、+6shake upしたpaoさん。
でした。 f:id:atma_inc:20200125185358j:plain f:id:atma_inc:20200125185451j:plain

懇親会

東京、大阪かかわらず、至る所で今回の解法について議論がされていました。 f:id:atma_inc:20200125193057j:plain f:id:atma_inc:20200125195318j:plain

まとめ

今回、東京と大阪の同時開催という、難しい環境の中で無事コンペが終了できましたのも、参加いただいた皆様のご協力及び、
データ及び会場のご提供を頂きましたサイバーエージェント様のおかげです、遅くなりましたが改めてお礼申し上げます。 f:id:atma_inc:20200125192101j:plainf:id:atma_inc:20200125190100j:plain 次回も予定しておりますので今回ご参加いただいた方も、今回でご興味を持って頂いた方も是非ご参加お待ちしております!!

atmaCup#2開催レポート(運営編) #atmaCup

はじめに

11/23(金)にatmaCupの3回目となるatmaCup#2を開催し、無事終了しました。

本記事では、当日の模様など写真を元にまとめてみました。

今回は総勢39人にご参加いただき、Kaggle Grand Master2名を筆頭にKaggleでも成績を残されている実力者ぞろいの大会となりました。

f:id:atma_inc:20191128003124p:plain

参加者のレポ

参加いただいた方で当日のレポを書いていただけた方もいらっしゃいましたので、こちらも合わせてチェックください。

takapyさんのブログ

www.takapy.work

paoさんのブログ

pao2.hatenablog.com

弊社インターンの植田君の記事

atma.hatenablog.com

会場の様子

今回会場はパソナテックさんのお洒落な会場をお貸しいただきました。 広々とした会場で皆様不自由無く解析できたかと思います。

f:id:atma_inc:20191127180446j:plainf:id:atma_inc:20191127175219j:plain

開会

弊社nyk_gotoよりオープニング。

f:id:atma_inc:20191127154620j:plainf:id:atma_inc:20191127154624j:plain

本日のデータの説明などが行われました。

また、#0と#1の優勝者に物理メダルが付与されました。

f:id:atma_inc:20191127154823j:plainf:id:atma_inc:20191127154828j:plain

コンペ開始

コンペが開始されました。 皆さん集中して解析しています。

f:id:atma_inc:20191127182041j:plain

こだわりのコーヒー提供

弊社の社員兼カメラマン兼バリスタの下村がハンドドリップ、フレンチプレス、エアロプレスなどさまざまな方法で抽出したコーヒーを提供しました。

f:id:atma_inc:20191127183140j:plainf:id:atma_inc:20191127183256j:plain

初心者向け講座vol.1 はじめてのSubmission

弊社nyk_gotoより初心者向け講座の1回目が行われました。

f:id:atma_inc:20191127185754j:plain

お昼ごはん

Submissionして一段落ついた方からお昼ごはんタイム

ランチスポンサーのiPlugさんよりお弁当の提供です。

f:id:atma_inc:20191127185348j:plain

初心者向け講座vol.2 lightGBMによるmodelとsubmitの作成

弊社nyk_gotoより初心者向け講座の2回目が行われました。 lightGBMによるmodelとsubmitの作成について講座がありました。

f:id:atma_inc:20191127190633j:plainf:id:atma_inc:20191127190615j:plain

常に変動する順位

#1では常にtakuokoさんがトップを走り続けていましたが、今回は上位が見るたびに変動があり、 誰が優勝してもおかしくない展開に!

AutoMLは今回振るわず10位あたりをうろうろしてます。

大詰め

デッドラインまであと1時間、チームマージも締め切り皆さんの集中力もピークに。

f:id:atma_inc:20191127200339j:plainf:id:atma_inc:20191127200227j:plain

コンペ終了

18:00にコンペが終了しました。 みなさん緊張から開放されゆったりされていました。 このタイミングでのPublic1位は弊社インターンを含むチームが1位でした。

f:id:atma_inc:20191127200952p:plain

結果発表

結果はなんと!! 1位: Publicから、+3shake upしたpaoさん。
2位: Publicから、+5shake upしたjackさん。
3位: Publicから、-1shake downしたkurupicalさん、monnuさん、osciiartさんチーム。
でした。

PublicとPrivateの差に騒然となる中、 1〜3位まで方に賞金とメダルが、
nyk_goto賞(もっとも多くDiscussionでいいねをもらった方)に賞金が授与されました。

f:id:atma_inc:20191127201901j:plainf:id:atma_inc:20191127201853j:plain

懇親会

同会場で懇親会が行われました、今回ランチスポンサーしていただいたiPlugの宮﨑さんの乾杯の音頭とともに懇親会が始まりました。

f:id:atma_inc:20191127202152j:plain

至る所で今回の解法について議論がされていました。

f:id:atma_inc:20191127202531j:plainf:id:atma_inc:20191127202535j:plain

Kaggle本サイン会も!?

その後有志の方々で2次会や麻雀会?など大阪の夜の街に消えていきました。

まとめ

次回は年明けまでに開催情報を公開できるように絶賛調整中です。 ご期待ください。