茨城エンジニアのPython開発日記

茨城のITベンチャー企業ではたらく2年目エンジニア。Pythonで色々なものを作成中。

Androidアプリ作成時、フラグメントにビューをできるだけコード上から配置する方法


ブログから記事を見つけたい場合はこちら

ブログ地図 - 茨城エンジニアのPython開発日記

こんにちは。ファイアーエムブレム風花雪月やりました。
あんな地獄を見せるくらいなら学園ものにするな。


さて、タイトルのようにandroidアプリ作成時にできるだけコードからビューを配置します。

0. 前提

Android Studioアプリ開発するとき、アプリ画面はActivity⇒Fragment⇒Viewという順番に配置されます。
Activityが一番土台側で、Viewが実際に配置されるボタンとか写真とかそんなのですね。

Viewを宣言やら配置するのをxmlでやらずに、javaのコード上でしたいなと思ったんですが意外と手間取りました。
ActivityクラスにはViewを配置するメソッドがあるんですが、Fragmentにはそれらしいメソッドが見つからなかったんですね。

ちなみにBottom Navigation Activityというテンプレートを使っています。

0. 解決方法

上記の解決策が下記です。
①Fragmentのxmlに最低限のビューを配置(背景画像とスクロールビュー)
②FragmentのonCreatedメソッドでスクロールビュー上にボタンやらのViewを配置

どうということはなく、「スクロールビュー」という自分自身にViewを配置できるViewをxmlで置いといただけです。
以下コード。変数名が適当なのは許してください。

●Fragmentのxml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.home.HomeFragment">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/bg"
        android:id="@+id/backGroundViewHome"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/scrollViewHome">
    </ScrollView>

</androidx.constraintlayout.widget.ConstraintLayout>

●Fragmentのクラス

package com.makty.bingogosyuin.ui.home;

import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;

import com.makty.bingogosyuin.R;
import com.makty.bingogosyuin.databinding.FragmentHomeBinding;

public class HomeFragment extends Fragment {

    private FragmentHomeBinding binding;

    private Context m_context = null;       /*コンテキスト*/

    private @ColorInt
    int mBackgroundColor = Color.TRANSPARENT;

    /**画面上のビュー**/
    ScrollView m_scrollView = null;

    Button mButton0_0 = null;
    Button mButton0_1 = null;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        HomeViewModel homeViewModel =
                new ViewModelProvider(this).get(HomeViewModel.class);

        binding = FragmentHomeBinding.inflate(inflater, container, false);
        View root = binding.getRoot();
        m_context = this.getContext();


        /*バックグラウンドのスクロールビュー取得*/
        m_scrollView = (ScrollView) root.findViewById(R.id.scrollViewHome);

        /*ボタン設置レイアウト作成*/
        LinearLayout layout = new LinearLayout(m_context);
        layout.setOrientation(LinearLayout.HORIZONTAL);

        layout.setLayoutParams(new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT));
        layout.setGravity(Gravity.CENTER);

        /*ボタン作成*/
        mButton0_0 = new Button(m_context);
        mButton0_0.setBackgroundColor(0xFFee7800);
        mButton0_0.setText("鹿島神社\n(茨城県)");
        layout.addView(mButton0_0);

        mButton0_1 = new Button(m_context);
        mButton0_1.setBackgroundColor(0xFFee7800);
        mButton0_1.setGravity(Gravity.CENTER);
        mButton0_1.setText("鹿島神社\n(茨城県)");
        layout.addView(mButton0_1);

        /*スクロールビューにレイアウトを追加*/
        m_scrollView.addView(layout);

        return root;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }
}


今回できた画面はこんな感じ。


めでたい。
言語の仕様や使い方をゆっくり自分のものにしていくときが一番心が落ち着きますね。

よいお年を。

KaiNDを作っていくぞ会 第5回 --MobileNetV2をGrad-CAMで可視化--


ブログから記事を見つけたい場合はこちら

ブログ地図 - 茨城エンジニアのPython開発日記



こんにちは。
開発メンバー、AI担当のKです。

最近、毎日簡易アエロバイクをこいでいるおかげか、6 [kg] 減量しました。
この調子でスマートになります。



目次

0. 前回まで

↓前回のブログ
https://tottorisnow33.hatenablog.com/entry/2022/07/23/094350

引き続き、KaiNDシリーズを拡張・改善しています。一緒にやっているメンバーで以下のように分担しています。

松原: KaiND Web
K   : AI自体の改善
Y   :スマホアプリのUIの改善


今回は、KaiNDを作っていくぞ会 第2回 --AI改善-- - 茨城エンジニアのPython開発日記 の続きで、可視化にチャレンジです。

1. 使用するNetwork

前回は可視化のためにResNetを使っていましたが、今回はKaiNDのアプリに載せているMobileNetV2で可視化します。
MobileNetV2は事前学習済みのものを用意して、転移学習でパグとブルドッグの識別をできるようにします。
MobileNetV2の転移学習については、以下を参考にしました。
www.tensorflow.org


2. Grad-CAMで可視化するために注意したこと

Grad-CAMで可視化する場合、Conv layerの出力が必要です。
値を取得するために、Conv layerの名前を指定する必要があります。
しかし、Tensorflow公式のやり方のように tf.keras.applications.MobileNetV2() の後に自分でlayerを追加すると、Conv layerを指定できません。
model.summary()で見ると分かるのですが、MobileNetV2のところが、"mobilenetv2_1.00_224"という1つの塊になってしまいます。

# Tensorflowの公式チュートリアルを参考にしたソース
IMAGE_SIZE = (224, 224)
IMG_SHAPE = IMAGE_SIZE + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet')
base_model.trainable = False

inputs = tf.keras.Input(shape=IMG_SHAPE)
x = base_model(inputs, training=False)
# 層追加
x = tf.keras.layers.GlobalAveragePooling2D()(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = tf.keras.layers.Dense(y_train.shape[-1],
                          kernel_regularizer=tf.keras.regularizers.l2(0.0001), activation="softmax")(x)
model = tf.keras.Model(inputs, outputs)
model.summary()


Model: "model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) [(None, 224, 224, 3)] 0
_________________________________________________________________
mobilenetv2_1.00_224 (Model) (None, 7, 7, 1280) 2257984
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280) 0
_________________________________________________________________
dropout (Dropout) (None, 1280) 0
_________________________________________________________________
dense (Dense) (None, 3) 3843
=================================================================
Total params: 2,261,827
Trainable params: 3,843
Non-trainable params: 2,257,984
_________________________________________________________________


そこで、ソースを以下のように少し書き換えて、MobileNetV2のconv layerの名前を取得できるようにしました。
ポイントは、base_model.outputを使って、MobileNetV2の出力を追加したい層の入力とすることと、tf.keras.Modelのinputにはbase_model.inputを使うことです。

# アレンジしたソース
IMAGE_SIZE = (224, 224)
IMG_SHAPE = IMAGE_SIZE + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet')
base_model.trainable = False
x = base_model.output
# 層追加
x = tf.keras.layers.GlobalAveragePooling2D()(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = tf.keras.layers.Dense(y_train.shape[-1],
                          kernel_regularizer=tf.keras.regularizers.l2(0.0001), activation="softmax")(x)
model = tf.keras.Model(base_model.input, outputs)
model.summary()


Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
============================================================
input_1 (InputLayer) [(None, 224, 224, 3) 0
__________________________________________________________________________________________________
Conv1_pad (ZeroPadding2D) (None, 225, 225, 3) 0 input_1[0][0]
__________________________________________________________________________________________________
Conv1 (Conv2D) (None, 112, 112, 32) 864 Conv1_pad[0][0]
__________________________________________________________________________________________________
bn_Conv1 (BatchNormalization) (None, 112, 112, 32) 128 Conv1[0][0]
__________________________________________________________________________________________________
Conv1_relu (ReLU) (None, 112, 112, 32) 0 bn_Conv1[0][0]
__________________________________________________________________________________________________
expanded_conv_depthwise (Depthw (None, 112, 112, 32) 288 Conv1_relu[0][0]
__________________________________________________________________________________________________
expanded_conv_depthwise_BN (Bat (None, 112, 112, 32) 128 expanded_conv_depthwise[0][0]
__________________________________________________________________________________________________
expanded_conv_depthwise_relu (R (None, 112, 112, 32) 0 expanded_conv_depthwise_BN[0][0]
__________________________________________________________________________________________________
expanded_conv_project (Conv2D) (None, 112, 112, 16) 512 expanded_conv_depthwise_relu[0][0
__________________________________________________________________________________________________
expanded_conv_project_BN (Batch (None, 112, 112, 16) 64 expanded_conv_project[0][0]
__________________________________________________________________________________________________
block_1_expand (Conv2D) (None, 112, 112, 96) 1536 expanded_conv_project_BN[0][0]
__________________________________________________________________________________________________
block_1_expand_BN (BatchNormali (None, 112, 112, 96) 384 block_1_expand[0][0]
__________________________________________________________________________________________________
block_1_expand_relu (ReLU) (None, 112, 112, 96) 0 block_1_expand_BN[0][0]
__________________________________________________________________________________________________
block_1_pad (ZeroPadding2D) (None, 113, 113, 96) 0 block_1_expand_relu[0][0]
__________________________________________________________________________________________________
block_1_depthwise (DepthwiseCon (None, 56, 56, 96) 864 block_1_pad[0][0]
__________________________________________________________________________________________________
block_1_depthwise_BN (BatchNorm (None, 56, 56, 96) 384 block_1_depthwise[0][0]
__________________________________________________________________________________________________
block_1_depthwise_relu (ReLU) (None, 56, 56, 96) 0 block_1_depthwise_BN[0][0]
__________________________________________________________________________________________________
block_1_project (Conv2D) (None, 56, 56, 24) 2304 block_1_depthwise_relu[0][0]
__________________________________________________________________________________________________
block_1_project_BN (BatchNormal (None, 56, 56, 24) 96 block_1_project[0][0]
__________________________________________________________________________________________________


.....[中略]......


block_16_expand (Conv2D) (None, 7, 7, 960) 153600 block_15_add[0][0]
__________________________________________________________________________________________________
block_16_expand_BN (BatchNormal (None, 7, 7, 960) 3840 block_16_expand[0][0]
__________________________________________________________________________________________________
block_16_expand_relu (ReLU) (None, 7, 7, 960) 0 block_16_expand_BN[0][0]
__________________________________________________________________________________________________
block_16_depthwise (DepthwiseCo (None, 7, 7, 960) 8640 block_16_expand_relu[0][0]
__________________________________________________________________________________________________
block_16_depthwise_BN (BatchNor (None, 7, 7, 960) 3840 block_16_depthwise[0][0]
__________________________________________________________________________________________________
block_16_depthwise_relu (ReLU) (None, 7, 7, 960) 0 block_16_depthwise_BN[0][0]
__________________________________________________________________________________________________
block_16_project (Conv2D) (None, 7, 7, 320) 307200 block_16_depthwise_relu[0][0]
__________________________________________________________________________________________________
block_16_project_BN (BatchNorma (None, 7, 7, 320) 1280 block_16_project[0][0]
__________________________________________________________________________________________________
Conv_1 (Conv2D) (None, 7, 7, 1280) 409600 block_16_project_BN[0][0]
__________________________________________________________________________________________________
Conv_1_bn (BatchNormalization) (None, 7, 7, 1280) 5120 Conv_1[0][0]
__________________________________________________________________________________________________
out_relu (ReLU) (None, 7, 7, 1280) 0 Conv_1_bn[0][0]
__________________________________________________________________________________________________
global_average_pooling2d (Globa (None, 1280) 0 out_relu[0][0]
__________________________________________________________________________________________________
dropout (Dropout) (None, 1280) 0 global_average_pooling2d[0][0]
__________________________________________________________________________________________________
dense (Dense) (None, 3) 3843 dropout[0][0]
============================================================
Total params: 2,261,827
Trainable params: 3,843
Non-trainable params: 2,257,984
__________________________________________________________________________________________________


これで、Conv layerの名前を取得できるようになりました。


後は↓の参考ソースのように、Conv layerのLayer Name (とモデルのoutput) から勾配を取る感じです。
Grad CAM implementation with Tensorflow 2 · GitHub

3. MobileNetV2でGrad-CAMをやってみた結果

自作ResNetでやってみた結果は↓です。



4. 今後

Grad-CAMを使って可視化をすることができるようになったので、
次回からは推論結果を定量的に評価する機構も作りたいと思います。
そして、どんな画像が苦手なのかについて、Grad-CAMの結果と定量的評価結果から調べ、苦手そうなデータを学習データに追加したり、MobileNetV2よりも性能が良いとされるモデルならどうか評価してみたり、、、
などをしてKaiNDの性能を上げていきたいです。

CSVの列を入れ替えるソフト作ってみた


ブログから記事を見つけたい場合はこちら
ブログ地図 - 茨城エンジニアのPython開発日記

CSVの列を入れ替えるソフト作りました。


コードは美しくないけど内製便利化ツールならええだろという気持ち。

import os
import csv


#inputフォルダのファイル一覧取得
input_dir_path = "./input"
files = os.listdir(input_dir_path)


#inputフォルダ内のファイルでループ
for file_name in files:
    
    input_file_path = input_dir_path + "/" + file_name
    output_file_path = "./output" + "/" + file_name


    #ファイルを開く
    with open(input_file_path) as f:
        
        #ファイル内を一行ずつループ
        for line in f:


            #改行文字消去
            line = line.replace("\n""")


            #行を,で分割
            values = line.split(",")


            #出力する行を作成(ここで並び替える)
            output_values = []
            output_values.append(values[9])
            output_values.append(values[8])
            output_values.append(values[7])
            output_values.append(values[6])
            output_values.append(values[5])
            output_values.append(values[4])
            output_values.append(values[3])
            output_values.append(values[2])
            output_values.append(values[1])
            output_values.append(values[0])


            #出力CSVに書き込み
            f_out = open(output_file_path, 'a')
            writer = csv.writer(f_out)
            writer.writerow(output_values)

特に語ることもないので終わり。

KaiNDを作っていくぞ会 第4回 --API作成--

ブログから記事を見つけたい場合はこちら

 

ブログ地図 - 茨城エンジニアのPython開発日記

 

こんにちは松原です。

立ちながら人間と話す機会があって疲れ果てました。

ソフトウェアエンジニアは貧弱。

 

0. 前回まで

KaiNDを作っていくぞ会、以下の3つの柱で進めています。

 

・Webアプリ作成: 松原

スマホアプリの改善: Y

・AI自体の改善: K

 

今のところ技術を学ぶだけで楽しいけど、

そのうちそれなりのシステムとして形にしたいなあ。

 

 

1. WebアプリUI

フロントの様子

アップロードボタン押下で画像選択

APIのあるフォルダ以下にアップロードされる

で、あとはAPI側に受け渡された画像をtensorflowで識別していきます。

2. システム構成図

割としょうもないですがシステムの構成図書いておきます。簡単ですね。

3. 今後の展望

API側に画像を渡すことができたので、golangでの識別処理を実装していきます。

 

最近はずっと自宅サーバ欲しいなあと思っています。

なんなら今のPCをサーバにするから仕方ないということでPC新調したい。

 

それではまた。

KaiNDを作っていくぞ会 第3回 --UI改善--

ブログから記事を見つけたい場合はこちら

 

ブログ地図 - 茨城エンジニアのPython開発日記

 

こんにちは。

UI担当のYです。

パリピ孔明終わっちゃいました。

0. 前回まで

KaiNDを作っていくぞ会、前回はAI改善でした。

tottorisnow33.hatenablog.com

 

KaiNDアプリは、前々回から以下のように担当を振って回しています。

 

松原: KaiND Web

K   : AI自体の改善

Y   :スマホアプリのUIの改善

 

今回は私YがスマホアプリのUI改善について書いていきたいと思います。

 

1. UI改善ポイント

リリース当初のアプリ画面です。

リリース時のUI

 

このままだと拡張性がないので、とりあえずハンバーガーメニューの実装をしていきたいと思います。

機能はあとで考えます。

画像保存したり、カメラロールの画像を識別できる機能とかつけたい。

2. ハンバーガーメニューの実装

一から作ると骨が折れるので、AndroidStudio内で用意されているテンプレートを使用していきます。

 

新しいプロジェクトを作成しようとすると、以下のようにテンプレートが選べます。

プロジェクト作成画面


リリース時のアプリを作った時は、余計な機能を入れたくなかったのでEmpty Activity  を使用していましたが、今回は Navigation Drawer Activity を使用していきます。

他にも地図作ったり、ログイン画面とかも簡単に作れるっぽい。

 

 

こんな感じですぐにハンバーガーメニューの実装ができました。

カメラ映像の取得でエラーが出てしまったので一旦アイコンの画像を貼り付けています。

 

デフォルトだと、画面ごとのxmlファイルがあって、それに対応するjavaファイルがあるみたいです。

画面ごとのjavaファイルをいじって機能を実装していく感じですね。

フォルダ構成

3. 次回

とりあえず、過去ブログを参考にカメラ映像の取得をできるようにします。

そしたらタイトルウィンドウの色変えたりとかいろいろやっていきたいです。

 

来週はWebアプリ担当松原からの報告になります。

KaiNDを作っていくぞ会 第2回 --AI改善--


ブログから記事を見つけたい場合はこちら

ブログ地図 - 茨城エンジニアのPython開発日記



こんにちは。
開発メンバー、AI担当のKです。

テレワークが続いているので重度の運動不足です。
ここ最近、簡易的なエアロバイクをGETしたので、毎日使っています。
これで少しでも運動不足を解消したいです。



目次

0. 前回まで

↓前回のブログ
tottorisnow33.hatenablog.com

前回からKaiNDシリーズを拡張・改善しています。一緒にやっているメンバーで以下のように分担しています。

松原: KaiND Web
K   : AI自体の改善
Y   :スマホアプリのUIの改善

前回は松原くんがWebアプリについてブログに載せてくれました。今回は私KによるAI自体の改善について書いていきます。


1. AIがどこを注目しているか可視化したい

リリースしたアプリ(KaiNDシリーズ -Pug or Bulldog-)は、スマホのカメラでかざしたものがパグなのかブルドッグなのかその他なのかを識別するものです。
play.google.com


これからもっと性能を上げたいと思っています。
性能を上げるためには、学習データセットを増やす、Data Augmentationをもっとかける、もっと良いネットワークを使う(今はMobilnetV2を使用)などがあると思います。
何から試すべきか決めるためにも、まずはAIが画像上のどこに注目して、パグ or ブルドッグと判定しているのかを可視化することにしました。


可視化のために、Grad-CAMという論文の手法を使いました。
https://openaccess.thecvf.com/content_ICCV_2017/papers/Selvaraju_Grad-CAM_Visual_Explanations_ICCV_2017_paper.pdf


Grad-CAMは、"grad"という名前がついているように勾配を使います。
最終出力と最後のConv layerの出力で勾配を取る(微分する)ことで、conv layerの特徴マップのどの辺が最終出力に寄与しているかを計算しています。
"最終出力"と言っているのは、パグとかブルドッグとかの結果を出すクラス識別結果の部分です。
一方、"最後のConv layer"というのは、全結合層を適用する前(CNN部分のおしり)の部分です。
Grad-CAMの詳細は、論文や、以下のサイトを参考にしていただけると良いかと思います。
以下のサイトではGrad-CAMの手法を、図を使いながら説明しています。
dajiro.com



2. 参考にしたソース

私たちは Tensorflow の2系を使っています。
同じくtfの2系を使っていてGrad-CAMを実装している人はいないかなと探したところ、見つけました。
Grad CAM implementation with Tensorflow 2 · GitHub

↑こちらのソースを参考にGrad-CAMを実装しました。



ただ、実装するにあたり、アプリで使っているMobileNetV2のソースではうまくいかなそうなので、以前自作したResNetを使いました。
tottorisnow33.hatenablog.com


なぜMobileNetV2のソースではうまくいかなかったのか。
私たちのMobileNetV2のソースでは、以下のブログで書いたようにtensorflow hubを利用しています。

tottorisnow33.hatenablog.com


Section 1で書いたように、Grad-CAMではConv layerの出力が必要となります。
しかし、tensorflow hubで引っ張ってきたモデルは、画像の入力から全結合層による出力まで1つの箱のようになっているみたいでなのです。
私はtensorflow hubで引っ張ってきたモデルからconv layerのところだけ取り出せないかと試しましたが、ちょっとダメそうでした。
一方で、自作したResNetなら各layerを1つ1つ書いていて、conv layerの出力を取り出すのは容易だったのでResNetのソースでGrad-CAMを試しました。

3. 自作ResNetでGrad-CAMをやってみた結果

自作ResNetでやってみた結果は↓です。

色の付き方だけ見ると、パグの耳、目、口元あたりに注目していることがわかります。
ただ、、、↑のツイートに書いたように、実際にGrad-CAMで計算した値は、青い部分と赤い部分で割と同じような値でした。
具体的に言うと、赤いところが1.09、青いところが1.06とかです。
(ちなみに、Grad-CAMの計算結果が大きいほど出力に寄与しています。可視化するときは、最大値を赤、最小値を青となるように正規化してます。)


値が近いので、結局赤いところも青いところも同じくらい注目しているのでは...?
と思いましたが。
これで良いのだろうか。。。。


今回Grad-CAMを初めて使ったので、Grad-CAMで出力した数値の勘所がわかりません。。
そもそも、今回のResNetは事前学習もしてないので、このモデルが微妙なだけかもしれません。。

4. 今後

今回は、AIが画像のどこに注目してパグ or ブルドッグと言っているのか可視化するために、Grad-CAMを使ってみるところまでやりました。
ただ、section 3で書いたように、Grad-CAMの計算結果が似たような値なのがOKなのかわかりません。
また、本当はアプリに入れているMobileNetV2でやりたいところを、事前学習もしていないResNetで試してしまいました。
次は、アプリに入れているモデルと同じく、事前学習されたMobileNetV2をパグ/ブルドッグの識別用にファインチューニングしたモデルを用意して、Grad-CAMを試してみます!

KaiNDを作っていくぞ会 第1回 --Webアプリ--

ブログから記事を見つけたい場合はこちら

 

ブログ地図 - 茨城エンジニアのPython開発日記

 

こんにちは。松原です。

埼京線はだめです。

0. 前回まで

さて、前回はアプリをリリースしました。

https://play.google.com/store/apps/details?id=com.KaiND_PugOrBulldog.android_with_opencv

 

こちらです。いいアプリですね。アイコンはないけど。

で、今回からはKaiNDシリーズを育てていきます。

割り当てはとりあえず以下です。

 

松原: KaiND Web

K   : AI自体の改善

Y   :スマホアプリのUIの改善

 

1. Webアプリ要件

仮だけど画面と機能はこんな感じ

 

 

2. Webアプリの構成

バックエンド:      go

フロントエンド:   React.ts

 

で行こうかなと思ってます。

golangでつくったapi叩くと識別結果がレスポンスで帰ってくる感じですね。

データベースはとりあえず要らないかな。

 

tensorflowをgoで使うことも可能らしい。多分簡単にできる。

さすがgoogle

golangでtf

3. 今後の展望

はい。というわけでKaiNDのWebアプリ担当松原からのご報告でした。

来週はAI担当Kからの報告になります。

明日は焼肉です。