Elastic Beanstalk config ファイルでどハマりしたお話

お疲れ様です。
みなさまいかがお過ごしでしょうか。

コロナウィルスにより、大変な世の中になってしまい、今後どうなっていくのか不安に思っています。

弊社は2月末よりテレワークとなりました。
一部は出社している日もあるようですが、基本的にはもうみんなの顔を忘れている頃だと思います。
私はお家時間が増えたので、FODを契約し、のだめカンタービレのドラマと映画を一気にみました。
今クラシックを聞きながら書いています。
一曲もわかりません。

本題です。
私は2 ~ 3年ほど前からElasticBeanstalk + CircleCI or GitLabCI or GitHubAction という構成を多く使用しています。
色々な構成内容をconfigファイルに記載しておき、
developとproductionをブランチによって自動で切り替えたり、環境構築を人依存しないようにしております。

近頃スタートしたプロジェクトの開発環境を構築しようと、以前のプロジェクトから色々コピーしました。
ハマりました。
今回はNode.js + nginx 環境です。

http → https のリダイレクトを nginx で設定しています。

files:
 /etc/nginx/conf.d/redirect.conf:
 mode: "000644"
 owner: root
 group: root
 content: |
# Redirect HTTP To HTTPS
 server {
 listen 81;
 rewrite ^ https://$host$request_uri permanent;
 }

こんな感じのファイルを
.ebextensions/redirect.config

として置いておけば、あとはElastic Beanstalk君がやってくれていました。

...ところが新しく作った環境だとリダイレクトされない...  

...ファイルの記載方法が悪いのか...  

...ログみても何も残っていない...  

...とりあえず、SSH Key作って接続して中身みてみよう...

あれ...
/etc/nginx/conf.d/redirect.conf
作られてねーじゃん。

なんで無視するの?嫌われた?
という壁にぶち当たりました。

色々調べていくと...
https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/platforms-linux-extend.html

どうやら Linux
というプラットフォームでは nginx などの設定は

.platform/nginx/conf.d

にそのままの記載方法

server {
 listen 81;
 rewrite ^ https://$host$request_uri permanent;
}

これを custom.conf などに記載するように共通化されたみたいでした。

確かに元々のプロジェクト達は
Node.js running on 64bit Amazon Linux/4.14.1

今回は
Node.js 12 running on 64bit Amazon Linux 2/5.0.1

 でした。
これか!
となりました。(何かログ出してくれると助かったけど...)

AWSの進化は早いので、統一して全てのプロジェクトあげていこう!
と思いました。

他の困った方の役に少しでも立てれば幸いです。

Python × Selenium で自動化

元素周期表の覚え方「水兵リーベ僕の船...」懐かしいですね。
その元素記号の34番目は「Se」(Selenium)です。

一方、ITにおけるSelenium

  1. Webページにアクセスしてログイン

  2. 対象データを検索

  3. 必要な情報を入力して登録

というような、普段Webブラウザで行っている操作を自動化することができます。
PythonでもこのSeleniumを利用することができます。

最新ニュース記事取得

最近気になるニュースは?

就活の面接でも良く聞かれます。選んだニュースにより、その人の感性や価値観、人柄、知的好奇心が分かるので、効果的な質問だと思います。

さて、今回は最新ニュース記事の取得・収集を自動化する方法をご紹介します。(スクレイピングと言われているものです)

1時間毎にGoogleの「AI」最新ニュース記事を取得。それをSlackに投稿し、Excelに保存する。
の流れです。皆さんもサクッと試してみましょう。

1. 初期設定

pipコマンドを使って、Seleniumをインストールします。
$ pip install selenium

Google Chrome版のWebDriverをダウンロードします。
ChromeDriver - WebDriver for Chrome

2. ライブラリの定義

ここからはプログラミングです。
PythonSeleniumを使うために必要なライブラリを定義します。

  • Slackに投稿
  • Excelに保存
  • 定期実行

これらの処理のために必要なライブラリもあわせて定義します。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys

import requests
import datetime
import time
import cv2
import openpyxl
import schedule

3. WebDriverのオプション設定

Google Chrome版のWebDriverの設定です。headlessモードにすると、画面表示なしで動作します。自分のPC作業に影響せずに裏で動いてくれるので助かります。

DRIVER_PATH = './chromedriver'
options = Options()
options.add_argument('--headless')
driver = webdriver.Chrome(executable_path=DRIVER_PATH, options=options)

4. 定数の定義

検索キーを変更すれば、別のニュース記事を取得できます。

SearchKey = 'AI' #ニュースの検索キー
Token = 'XXXX-XXXX-XXXX-XXXX' #Slackで取得したトークン
Channel = 'XXXX' #SlackのチャンネルID
Excelfile = 'LatestNews.xlsx' #Excelファイル名

5. メイン処理

URL指定で最初からニュース一覧の画面に遷移させることもできますが、テンプレートとして他でも流用できるようにあえて検索キーを入力しての画面遷移としております!

def job():
  # Googleのページを開く
  driver.get("https://www.google.co.jp/")

  # 検索Boxのelementを取得
  search_box = driver.find_element_by_class_name("gLFyf")

  # 検索Boxにキーを入力
  search_box.send_keys(SearchKey)

  # Enterキーを入力
  search_box.send_keys(Keys.ENTER)

  # Google検索結果画面のニュースタブのelementを取得
  link_news = driver.find_element_by_class_name("q")

  # ニュースタブをクリック
  link_news.click()

  # 最新ニュース記事のリンクのelementを取得
  link_news_latest = driver.find_element_by_class_name("l")

  # 最新ニュース記事のリンクをクリック
  link_news_latest.click()

  # 最新ニュース記事画面の幅と高さを取得(全画面のスクショを撮るため)
  page_width = driver.execute_script('return document.body.scrollWidth')
  page_height = driver.execute_script('return document.body.scrollHeight')

  # 最新ニュース記事画面の幅と高さをセット
  driver.set_window_size(page_width, page_height)

  # 画面読み込みのため待機
  time.sleep(20)

  # 現在日時取得
  dt_now = datetime.datetime.now()
  
  # スクショのファイル名定義
  screenshot_path = "Pictures/" + str(dt_now) + ".png"

  # スクショ保存
  driver.save_screenshot(screenshot_path)

  # スクショのPNG画像を圧縮
  img = cv2.imread(screenshot_path)
  cv2.imwrite(screenshot_path, img, [cv2.IMWRITE_PNG_COMPRESSION, 9])

  # 画面タイトルを取得
  cur_url_title = driver.title

  # 画面URLを取得
  cur_url = driver.current_url

  # Slackに投稿(画面タイトル, 画面URL, スクショ)
  files = {'file': open(screenshot_path, 'rb')}
  param = {
    'token':Token,
    'channels':Channel,
    'initial_comment': SearchKey + "最新ニュース" + "\n\n" + cur_url_title + "\n\n" + cur_url,
    'title': screenshot_path
  }
  requests.post(url="https://slack.com/api/files.upload",params=param, files=files)

  # Excelの最終行に追記(時間, 画面タイトル, 画面URL, 検索キー)
  wb=openpyxl.load_workbook(Excelfile)
  sheet = wb.active
  max_row = sheet.max_row
   
  list = [dt_now, cur_url_title, cur_url, SearchKey]
  for index, item in enumerate(list):
    sheet.cell(row=max_row+1,column=index+1).value = item
  wb.save(Excelfile)

6. 定期実行処理

scheduleライブラリを使用すると手軽にジョブの設定ができます。

# 3分毎に実行
# schedule.every(3).minutes.do(job)
# 1時間毎に実行
schedule.every().hour.do(job)

while True:
  schedule.run_pending()
  time.sleep(1)

以上です!シンプルですね。

最後に

今回は、最新ニュース記事取得(スクレイピング)を例としてご紹介しましたが、WebシステムのUIテスト自動化にも使えます。画面遷移や、項目が多い入力フォームの検証にはもってこいです。単純な繰り返し作業はSeleniumにお任せしちゃいましょう!

株式会社ブリスウェル

キュウリ収穫量の可視化

キュウリを植えたらキュウリと別の物ができると思うな。人は自分の植えたものを収穫するのである。
二宮尊徳(金次郎)

変わらなければ生き残れない
ドラスティックに何かを変えなければ...
そんな危機感が日々日々強くなっていきます。

ただやはりキュウリからはキュウリである。自分自身に今あるものを育てて、いかに美味しく生き生きとしたものにしていくか。こんな時だからこそしっかりと学び、またやってくる旬の時期に備えたいですね。

とキュウリキュウリ言っていますが、僕はキュウリが大好きです。浅漬け、ピリ辛、梅あえ、シンプルなもろきゅう&味噌マヨもたまりません。宮崎県のキュウリの冷汁も大好きです。

今回はキュウリでいきましょう!

f:id:KenjiU:20200504130909p:plain

キュウリの収穫量を可視化してみました。なんとPythonによるWebアプリで実現しています。

PythonでWebアプリ作成

Pythonといえば機械学習ディープラーニング用。そんな風に考えていらっしゃる方も多いかと思います。しかし、Pythonには様々なライブラリが存在しており、実はWebアプリも作成することが可能です。

では、コードを見ていきましょう。

1. ライブラリの定義

定義しているDashPython用のWebアプリを作成するためのフレームワークです。Bootstrap(HTML, CSS, JavaScript フレームワーク)やPlotly(グラフライブラリ)を使うことができます。

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
import dash_core_components as dcc
import numpy as np
import pandas as pd
import plotly.graph_objects as pgo

# Bootstrapスタイルシートをリンク
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])

2. データの取得

キュウリの収穫量データと都道府県の緯度経度データを読み込みます。
以下のサイトのデータを使用いたしました。

きゅうり(キュウリ・胡瓜)の都道府県別生産量(収穫量)/グラフ/地図/一覧表|統計データ・ランキング|家勉キッズ

【みんなの知識 ちょっと便利帳】都道府県庁所在地 緯度経度データ - 各都市からの方位地図 - 10進数/60進数での座標・世界測地系(WGS84)

# きゅうりの都道府県別収穫量データ(CSVファイル)を読み込む
# 引用: ieben.net | きゅうりの生産量(収穫量)
# https://ieben.net/data/production-vegetables/japan-tdfk/k-kyuuri.html
ccb = pd.read_csv('cucumber_jpn.csv')

# 都道府県の緯度経度データ(Excelファイル)を読み込む
# 引用: みんなの知識 ちょっと便利帳 | 都道府県庁所在地 緯度経度データ
# https://www.benricho.org/chimei/latlng_data.html
ccb_latlng = pd.read_excel("latlng_data.xls", header=4)
ccb_latlng = ccb_latlng.drop(ccb_latlng.columns[[0,2,3,4,7]], axis=1).rename(columns={'Unnamed: 1': '都道府県'})
ccb_latlng = ccb_latlng.head(47)

3. データをグラフ表示用に加工

2で読み込んだデータを加工します。

# 収穫量分布バブルチャート用
# 「きゅうりの都道府県別収穫量データ」と「都道府県の緯度経度データ」をマージ
ccb_merge = pd.merge(ccb, ccb_latlng, on='都道府県')
# バブルチャートにカーソルをあてた時に表示されるテキストを定義
ccb_merge['notes'] = np.nan
for i in range (len(ccb_merge)):
  ccb_merge['notes'][i] = ccb_merge['都道府県'][i] + ' / ' + str(ccb_merge['収穫量'][i]) + 't' + ' / ' + str(ccb_merge['順位'][i]) + '位'

# 収穫量トップ20グラフ用
ccb_prep = ccb.sort_values("収穫量",ascending=False).head(20)
ccb_prep = ccb_prep.sort_values("収穫量")

4. 画面表示処理

各エリアのサイズや色やテキストなどを仕様に従い定義します。とてもシンプルですね。

app.layout = dbc.Container(
[
  # 画面のタイトルエリア
  dbc.Row(
  [
    dbc.Col(
    html.H1("キュウリの収穫量グラフ"),
    style = {
    "size": "30px",
    "backgroundcolor": "#fffcdb",
    "color": "#00a95f",
    "textAlign": "left",
    }
    )
  ],
  ),
  # グラフのタイトルエリア
  dbc.Row(
  [
    # 左グラフのタイトル
    dbc.Col(
    html.H4("収穫量分布"),
    width = 7,
    style = {
      "height": "100%",
      "backgroundcolor": "white",
      "textAlign": "left",
      "padding":"10px"
      },
    ),
    # 右グラフのタイトル
    dbc.Col(
    html.H4("収穫量トップ20"),
    width = 5,
    style = {
      "height": "100%",
      "backgroundcolor": "white",
      "textAlign": "left",
      "padding":"10px"
      },
    ),
  ],
  ),
  # グラフエリア
  dbc.Row(
  [
    # 左グラフ
    dbc.Col(
    dcc.Graph(
    id = 'JpnMap',
    figure = {
    'data' : [
      pgo.Scattergeo(
      lat = ccb_merge["緯度"],
      lon = ccb_merge["経度"],
      marker = dict(
        color = 'rgb(0, 169, 95)', #バブルチャートの色
        size = ccb_merge['収穫量']/1500+5, #バブルチャートのサイズ
        opacity = 0.7 #バブルチャートの透明度
        ),
      hovertext = ccb_merge['notes'], #カーソルをあてた時に表示されるテキスト
      hoverinfo = "text"
      )
      ],
    'layout' : pgo.Layout(
      width = 600,
      height = 500,
      template="plotly_dark", #ダークモード
      margin = {'b':5,'l':5,'r':5,'t':5},
      geo = dict(
        resolution = 50,
        landcolor = 'white', #陸地の色
        lataxis = dict(
          range = [25, 47], #地図表示範囲(緯度)
        ),
        lonaxis = dict(
          range = [126, 150], #地図表示範囲(経度)
        ),
      )
    )
    }
    ),
    ),
    # 右グラフ    
    dbc.Col(
    dcc.Graph(
    id = 'Pref',
    figure = {
    'data' : [ 
      pgo.Bar(
      x=ccb_prep['収穫量'],
      y=ccb_prep['都道府県'], 
      orientation='h' #横棒グラフ
      ),
      ],
    'layout' : pgo.Layout(
      width = 400,
      height = 500,
      template = "plotly_dark", #ダークモード
      margin = {'b': 5, 'l': 5, 'r': 5, 't': 5},
      xaxis_title = "収穫量",
      yaxis_title = "都道府県"
      )
      }
    ),
    ),
  ],
  ),
],
)

5. アプリ起動処理

最後にWebアプリ起動処理です。Dashはホットリロード機能を持っており、コードに変更があった場合に、自動でブラウザをリフレッシュします。

# アプリを起動
if __name__ == "__main__":
  app.run_server(debug=True)

最後に

キュウリの生育適温は20〜25℃。暖かくなり始めた5月の連休頃が植え付け時期となります。夏の生育期のキュウリは1日3cm以上も大きくなります。実家の庭で、朝小さかったキュウリが昼過ぎにはすっかり大きくなっていて、びっくりしたこともありました。ベランダ菜園始めようかしら。

AIと芸術

ついつい最新ニュースが気になってしまう今日この頃。外出自粛ムードの三連休、皆さんいかがお過ごしでしょうか。

運動不足解消のため、僕はご当地版ラジオ体操で毎回笑いながら体操してます。世間では、あの爆発的なムーブメントを巻き起こしたビリーズブートキャンプが再燃しているようです。入隊しようかしら。

家トレも良いですが、ゆったりと音楽を聞いたり、映画を観たり、本を読んだりして過ごしている方も多いかと思います。
なんとそれらの芸術的な分野までも「AI」が担う時代が来るかもしれません。

今回はクリエイティブなことが不得意と言われてきた「AI」について、最近の芸術分野での活躍ぶりをまとめてみました。

音楽

2019年の第70回NHK紅白歌合戦で「AI美空ひばり」が大いに注目を集めました。

僕は歌声を聞いたとたん思わず涙が出てしまいました。子供の頃よくテレビから聞こえてきたあの歌声がふとよみがえってきたというのと、AIでここまでできるようになったのかという衝撃からだと思います。

www.yamaha.com

ヤマハさんの「VOCALOID:AI」(ディープラーニングを使った歌声合成技術)を用いて実現しているとのこと。

学習用データの美空ひばりさんの歌声ですが、美空ひばりさんは歌声のみのレコーディングはされていなかったそうで、伴奏が含まれた歌声データから伴奏部分を取り除く作業が容易ではなかったようです。

データの前処理はとても難しくコストもかかりますが「命」と言われるくらい学習には欠かせない工程になります。

www.itmedia.co.jp

こちらのITmediaさんの記事に、学習モデルについての説明があります。

 開発したシステムでは、複数の学習モデルを組み合わせて歌声を合成する。与えられた楽譜を読み込んで、音程を決めるモデルや発音のタイミングを決めるモデルといった歌声の特徴を作るものと、それらを組み合わせてコントロールするモデルや、最終的な波形を合成するモデルなどを段階的に使う。素片接続とは大きく異なる技術だ。

歌声合成のため、このような学習モデルを複数組み合わせているとは... 驚きの技術です。

「AI」と付くと、あたかも「AI」が自分で音楽を創り出すように感じますが、実際はインプットのデータ(学習用データ)をどうするか、どのようなモデルを用意するか、どのようなアウトプットを期待するかなど「人間」の創造的作業がとても重要になってきます。

漫画

AIと人間によって制作された手塚治虫さんの新作漫画「ぱいどん」が2月27日(木)発売の「モーニング」に掲載されました。

発売直後に購入しましたが、近未来の技術と人間が描かれており、シナリオ、絵、セリフどれも自然で読みやすく続編がとても気になる内容でした。はたして、どの部分をAIが担っているのでしょうか。

robotstart.info

こちらのロボスタさんの記事によると、AIはプロット(設定とあらすじ)とキャラクター(顔)を担当しているとのこと。

  1. プロット

    今回の作品はAIと人間の協業によるもので、ストーリーとマンガの書き起こしのほとんどは人間が行っている。AIが担当したのはまずは作品のプロット(設定とあらすじ)。手塚治虫氏が描いた漫画のストーリーを学習したAIがプロットだけを提示。それを元にスタッフが検討しながらストーリーを作り上げた。

  2. キャラクター

    NVIDIAが開発している人間の顔生成のシステムを応用。膨大な人間の顔画像を元にAIが人間の顔を生成する技術(おそらくGAN)のAIモデルをこのプロジェクトに活用することを決めた。人間の顔生成システムに手塚氏の漫画のキャラクターを追加学習する「転移学習」を行うと、成果がみるみる向上。その中から「ぱいどん」のイメージにぴったりの顔を採用した。

インプット(学習用データ)として、手塚治虫氏の作品データを読み込ませたり、はたまた人間の顔も利用したり、アウトプットについても人が選定して組み合わせていくなど、AIと人間の共同作業と言えるでしょう。人間の創造的作業をいかにデータ化(数値化)して学習用データとして利用できるかが難しいところですね。

俳句

空蝉や 揺れる草木を しかと抱き

こちらは祖母が詠んだ俳句です。昔、祖母の家の額縁に飾っており今でも心に残っております。つい最近、「空蝉」とは、古語の「現人(うつしおみ)」が訛ったもので「生きている人間の世界」を表すことを知り、地に足をつけて生きていこうという思いが強くなりました。

この感性が求められる俳句の作成についても、AIが挑戦しています。

AI俳句協会さんのサイトにAIが作成した俳句が載せられています。AIがどういう意図でその季語や言葉を選んだのか想像すると何か楽しいです。人間には生みだせない意外性が素敵ですね。

気になったAI俳句を一点ご紹介。

初便り 携帯電話 閉ざすのみ

ガラケーなのかなと、微笑ましい気持ちになります。

最後に

現状のAIでは、作品を勝手に生み出すことはできません。人間が、適切なデータを学習させたり、様々な条件を指定したり、評価・調整をしたりして、作品が生み出されます。

ただ、AIは過去のデータを大量に学習することができ、色々なパターンを試すことができます。その結果、人間には生み出せない何かを生み出せたり、人間が気付けなかったことを発見することがあるかもしれません。

人間が創造的作業を行うツールの一つとして、AIを上手く活用することができれば、AIならではの面白い共同作品が生まれてくるでしょう。

OpenAI Gymで強化学習

何かを始めようと思ったとき、まずは「それは前例のあることだろうか」と調べたり、他の人の意見を聞いたりすることがあるかと思います。

過去に同じような事例があると分かった場合、仲間を見つけたような安心感がでてきますね。確かにその事例を参考にすることで行動がしやすくなり、失敗するリスクを減らすことができます。

とはいえ、前例ばかり気にするのはよくありません。前例がないと行動できないのであれば、新しいものを世に送り出すことができません。前例がないからこそ、うまくいけば成功事例として注目されるわけです。

と誰よりも慎重派ビビリ属の僕が申し上げてしまいました。ただ「AI」ならばどうでしょうか。強化学習と呼ばれる手法により、失敗を恐れることなく試行錯誤を繰り返し、成功へと導いていきます。

では今回は、AI強化学習の手法を通して、新しいことへのチャレンジのコツを学んでいきましょう。

強化学習とは

強化学習」は、赤ちゃんが失敗を繰り返しながら歩き方を習得していくのと似てます。

www.youtube.com

こちらは、僕の大好きな「物理エンジンくん」さんの強化学習シミューレーション動画です。

  • 重心が前進する
  • 頭がある高さの幅にある

を「報酬」にして学習していますね。
強化学習を理解するには、「状態」「行動」「報酬」という3つの概念が重要です。

  1. 状態: 環境が今どのような状態か
  2. 行動: その状態での実際の行動
  3. 報酬: その行動によって獲得したスコア

赤ちゃんの歩き方習得を例にすると、「強化学習」では赤ちゃんのことを「エージェント」と言います。赤ちゃん(エージェント)は、まだ歩くことができない状態(=1. 状態)で歩こうとチャレンジし(=2. 行動)、それによってどれだけ歩けたか(=3. 報酬)がより大きくなるように頑張ります。

つまり、「強化学習」におけるエージェントは、「1. 状態」において何かしらの「2. 行動」を起こし、その行動から得られる「3. 報酬」を獲得するという処理を何度も行い、報酬の合計が一番大きくなるように学習していきます。

www.youtube.com

こちらは、もう一つの強化学習動画です。シュールですね。大好きです。

OpenAI Gymとは

さて、「物理エンジンくん」ほどのシミュレーションはできませんが、OpenAI Gymというオープンソース強化学習シミュレーション用プラットフォームを利用して、強化学習を試してみます。

山登りチャレンジ

OpenAI Gymの古典的な強化学習環境であるMountainCar-v0で山登りチャレンジをしましょう。

f:id:KenjiU:20200217185336g:plain
学習初期

2つの山の間にいる車(エージェント)が、前後に勢いをつけ、坂を登ろうとします。車が右の山の頂上(位置0.5)にあるゴールまで到達できたら、1エピソード終了となります。200ステップ内にゴールまで到達できなかった場合もタイムオーバーで1エビソード終了です。車は、速度が「0」、位置は「-0.6」〜「-0.4」のランダム位置から開始します。

報酬はステップ毎に「-1」(マイナスの報酬=罰)が与えられ、例えば200ステップ内にゴールできずタイムオーバーとなった場合は、トータル報酬は「-200」になります。強化学習は、このもらえる報酬ができるだけ大きくなるように行動する方法を学んでいきます。

今回は「Q学習」という強化学習アルゴリズムを使用します。

1. ライブラリと定数の定義

定数の中の「時間割引率」とは、すぐにもらえる小さい報酬を優先するか、将来にもらえる大きい報酬を優先するかを定義します。せっかち度ですね。「0」に近いほど将来の報酬は無視されて、「1」に近いほど将来の報酬が重視されます。

import gym
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.use('Agg') # matplotlibがAnti-Grain Geometry (AGG) を使う指定

# 定数
alpha = 0.2 # 学習係数
gamma = 0.99 # 時間割引率(0〜1)
epsilon = 0.002 # ε-グリーディ法のεの確率
num_divisions = 40 # 離散化時の分割数

2. 連続値から離散値に変換

連続値とは連続して無限に流れている「時間」のようなもの。また、離散値とは「人数」のように数えることができるものです。連続値である「位置」と「速度」を離散値に変換します。

def get_state(_observation):
    env_low = env.observation_space.low # 位置と速度の最小値
    env_high = env.observation_space.high # 位置と速度の最大値
    env_dx = (env_high - env_low) / num_divisions # 分割
    position = int((_observation[0] - env_low[0])/env_dx[0]) # 位置の離散値
    velocity = int((_observation[1] - env_low[1])/env_dx[1]) # 速度の離散値
    return position, velocity

3. Q関数を更新

経験(行動、状態、次の状態、報酬)に応じてQ関数を更新します。これにより、エージェントは時間の経過とともに、最適な行動をとれるようになります。

Q関数は以下のように表します。

Q(S_{t},a_{t})←Q(S_{t},a_{t})+α[r_{t+1}+γ\max_a Q(S_{t+1},a)-Q(S_{t},a_{t})]

  • S_{t}:時間tの時の状態
  • a_{t}:時間tの時の行動
  • α:学習係数
  • r_{t+1}S_{t+1}に遷移したときに得る報酬
  • γ:時間割引率
  • max_a Q(S_{t+1},a):次ステップの価値を最大化するQ関数
def update_q_table(_q_table, _action,  _observation, _next_observation, _reward, _episode):
    next_position, next_velocity = get_state(_next_observation)
    next_max_q_value = max(_q_table[next_position][next_velocity]) # 次ステップの価値を最大化するQ関数
    position, velocity = get_state(_observation)
    q_value = _q_table[position][velocity][_action]

    _q_table[position][velocity][_action] = q_value + alpha * (_reward + gamma * next_max_q_value - q_value)

    return _q_table

4. ε-グリーディ法

「ε-グリーディ法」という手法を用いて、現在の状態に応じた行動を選択するようにします。1-εの確率で最良の行動、εの確率でランダム行動を選択します。

現状に甘んじず、さらなる最適解を求めてランダムな行動を恐れることなくとるところが素敵ですね。

def get_action(_env, _q_table, _observation, _episode):
    if np.random.uniform(0, 1) > epsilon:
        position, velocity = get_state(observation)
        _action = np.argmax(_q_table[position][velocity])
    else:
        _action = np.random.choice([0, 1, 2])
    return _action

5. 動画保存

山登りチャレンジ結果を動画として保存します。

from JSAnimation.IPython_display import display_animation
from matplotlib import animation
from IPython.display import display

def display_frames_as_gif(frames):
    plt.figure(figsize=(frames[0].shape[1]/72.0, frames[0].shape[0]/72.0), dpi=72)
    patch = plt.imshow(frames[0])
    plt.axis('off')

    def animate(i):
       patch.set_data(frames[i])

    anim = animation.FuncAnimation(plt.gcf(), animate, frames=len(frames), interval=50)
    anim.save('movie_mountain_car.mp4')
    display(display_animation(anim, default_mode='loop'))

6. 学習実行

では、5000エピソードで学習を実行してみましょう。

if __name__ == '__main__':

    # 環境の生成
    env = gym.make('MountainCar-v0')

    # 初期化
    is_episode_final = False
    q_table = np.zeros((40, 40, 3))
    observation = env.reset()
    rewards = []
    history = []
    frames = []
    fig = plt.figure()

    # 学習
    for episode in range(5000):

        total_reward = 0
        observation = env.reset()
        
        if episode == 4999:
            is_episode_final = True

        for _ in range(200):
            # ε-グリーディ法で行動選択
            action = get_action(env, q_table, observation, episode)

            # 行動により、次の状態・報酬・ゲーム終了フラグを取得
            next_observation, reward, done, _ = env.step(action)

            # Q関数を更新
            q_table = update_q_table(q_table, action, observation, next_observation, reward, episode)
            total_reward += reward

            observation = next_observation

            # 最終エピソードの画面を描画
            if is_episode_final is True:
                frames.append(env.render(mode='rgb_array'))
            
            # ゲーム終了フラグがTrueになったら1エピソード終了
            if done:
                  if episode%100 == 0:
                      # ログ出力
                      print('episode: {}, total_reward: {}'.format(episode, total_reward))
                  rewards.append(total_reward)
                  history.append(total_reward)
                  plt.plot(history)
                  break

        if is_episode_final is True:
            # グラフを保存
            fig.savefig("img.png")
            # 動画を保存
            display_frames_as_gif(frames)

7. 実行結果

f:id:KenjiU:20200217185342g:plain
5000エピソード目

5000エピソード目を実行時の動画です。最初は全く登れる気配がなかったのに、強化学習の結果、悠々と登りきっています。

f:id:KenjiU:20200217184200p:plain
トータル報酬の推移

1〜5000エピソードのトータル報酬の推移グラフです。1000エピソードぐらいまでは、トータル報酬は「-200」となっており、200ステップ内で登りきれていませんが、1000エピソード以降はエピソードを積むにつれてより少ないステップで登れるようになっています。知的ですね。

最後に

現在、弊社も新規サービス立ち上げのために試行錯誤しております。強化学習のように、実施した結果をしっかりと評価、反省して次の行動につなげ、成功したとしてもその前例にとらわれず、更なる高みへ向けて果敢にチャレンジしていきたいと思います!

今回、参考にさせていただいた記事は以下です。ありがとうございました。
OpenAI Gym 入門 - Qiita
OpenAI Gym入門 / Q学習|npaka|note

AIのメリット・デメリット

映画「AI崩壊」が公開されましたね。早速観てきました。

AIとはどういうものなのか、またAIが日常生活に深く浸透してきた場合どのような問題が発生しうるか、がよく分かるかと思います。AI vs 人間ではなく、AIを取り巻く人間を中心に描かれており、AI時代に人間がなすべきことは何なのか、を考えさせられました。大沢たかおさん演じる桐生浩介の走りにも脱帽です。

これ以上は、ネタバレになってしまいますのでやめておきます。

さて、今回は、AIのメリット・デメリットについて書きます。僕たちも、AI製品の企画・開発を日々進めていますが、一度立ち止まって再認識したいと思います。

AIのメリット

①自動化による業務効率化

AIを活用することで、単調で同じ動作が繰り返される作業について、人間が実施するよりも早く正確にこなすことが可能になります。しかも、24時間365日動き続けることが可能です。

「RPA(ロボティック・プロセス・オートメーション)」が注目を集めて久しいですね。定型化できるPC業務について、手順を登録するだけで正確に自動処理を行うことができます。効率化のプロフェッショナルとも言えます。

ただ、いざRPAを導入したものの、十分に活用しきれないケースもあるようです。例えば、残業が多い部署の業務を自動化しようとRPAを導入したのに、残業の原因である業務が、人の判断、思考に依存するもの(例:顧客との関係性によって対応を変えなければならないもの)であれば意味がありません。

自動化できる業務を正しく見極める必要があります。

②安全性の向上

危険を伴う作業や、人間が立ち入ることができない場所での作業をAIに任せることができます。福島第一原子力発電所では、事故の安定化および廃炉の推進のために、ロボットが活用されています。

また、AIにより、道路のひび割れなどインフラの劣化や、機械の故障を自動的に検知することによって、事故を事前に防ぐことができ、適切なタイミングでメンテナンスを行うことができます。

転倒などの「ふらつき検知」、モノの「置き忘れ検知」など、個人の安全性に貢献する活用法も考えられています。

③創造的機会の創出

単調な作業をAIに置き換えることによって、人間はよりクリエイティブな作業や、人との高度なコミュニケーション力が求められている仕事に集中できる環境が生まれます。ただ、単純な作業とはいえ、いかにして効率的にこなすかを考えたり、やり遂げた後の達成感があったりするなど、AIに置き換えることで幸せかというとちょっと懐疑的です。また、以下のデメリットに記載していますが雇用の減少につながる可能性があります。

AIのデメリット

①雇用の減少

定型業務などがAIに置き換わることにより、人間が今まで行っていた業務量が減少し、雇用の減少がおきるのではと大きく懸念されています。 映画「AI崩壊」でも「AIに仕事が奪われる」ことを問題視しています。

②依存性

人間がAIに依存し、何事もAIの判断に委ねることになった場合、人間の判断能力は著しく低下してしまうでしょう。AIに意見を聞くぐらいの関係性が望ましいのかもしれません。

また、人間社会とAIが密接な関係になった場合、AIの管理にトラブルが発生すると、AIシステムと紐付けされている全ての環境に影響が出ることになります。もしも会社のメイン業務がAIと連携していれば、問題が解消するまで会社が機能しなくなる可能性もあります。そのため、万が一に備えたバックアップ体制を整えるなどのリスクマネジメントが重要となります。

③責任の所在

AIが問題を起こした場合、責任の所在をどうするか、という問題があります。「AIがそう判断したから」ではすまされません。法整備も必要ですが、判断、思考内容に透明性があり十分に検証可能なAIも求められます。

まとめ

今回は、AIについてのメリット・デメリットについてまとめました。

十分に検討が必要な課題もありますが、AIによって、確実に私たちの生活は便利になるでしょう。ただ、AIは自ら新しいものを生み出せません。いかにAIを使いこなし、私たちの生活を豊かで幸せなものにしていくかは人間次第です。これからは、AIのメリット、デメリットをよく理解して、うまく共存していくことが求められます。

あっと驚くサービスを弊社から出せるように頑張ります!

株式会社ブリスウェル

ロボットがすぐそばに

先日、池袋を歩いていたところ「ロボ酒場 期間限定オープン!」という看板を見かけました。

看板の写真をよく見てみると、工場などで見かけるロボットアームがマドラーを持っています。急いでいたので立ち寄ることはできませんでしたが
調べてみると・・・
池袋駅南口の養老乃瀧「一軒め酒場」内でAIロボットがお酒を提供してくれるとのことです。

しかも、お酒を作っている最中に顧客に話しかけてきて、顧客の反応を見て「いかにして笑わすことができるか」学んでいくとのこと。その場を和ませるユーモアさをどんどん鍛えていくロボット。うだつが上がらない漫才師?の僕もその学習能力を手に入れたいです。

【世界初】養老乃瀧がAIロボット酒場をオープン「ゼロ軒めロボ酒場」を体験してきた 省人化と笑顔の演出に QBITが開発 | ロボスタ

こちらのロボスタさんの記事には以下の動画もついています。

www.youtube.com

人間らしくはないけど、なぜか感情移入してしまいますね。ロボットというと、ついつい人間的なものを想像してしまいますが、このようにシンプルなものが方が親しみが出てくるような気がします。

ということで
今回は、前フリ通りに「ロボット」について、最近の技術、活用方法などについて書いていきます。よろしくお願いいたします。

ロボットビジョン

少子高齢化に伴い、労働力人口が減少しています。そのため、産業用ロボットを導入して労働力を確保し、生産性向上を図ろうとしている工場は少なくありません。

しかし、産業用ロボットを導入するためには、ロボットがどのように動くかプログラミングする(命を吹き込む)ティーチングが必要です。扱うワークを替える時にはカスタマイズも必要となってきます。経験者の力が必要です。

こうした課題を解決するために開発が進んでいるのが「ロボットビジョン」を搭載した産業用ロボットです。ロボットビジョンとは産業用ロボットの視覚機能で、カメラに映した画像を処理し、対象物を認識・判断したり、周囲の環境を把握したりする技術です。画像処理の結果でロボットに指示を出します。

ロボットビジョンを搭載したロボットは、ティーチングをはじめとした運用コスト削減につながるだけでなく、人間のように判断しながら作業を行うことができるので、より高度な作業が実現できます。

弊社では、画像認識処理の研究に力を入れており、今年はロボットビジョンにもチャレンジします!

協働ロボット

従来の「産業用ロボット」は「人間の代わり」に工場での作業を自動化することが主な用途でした。ただ、先日、製造業様の工場を見学させていただいたのですが、その時に見たロボットのあのスピード・正確さ・パワーを見ると、人間の代わりというより人間を遥かに超えているなと感じました。

近年では、安全柵なしで人間と一緒に作業を行うことが可能な「協働ロボット」に注目が集まっています。

以前は定格出力が80Wを超えるロボットを利用する場合、柵または囲い等を設ける規制がありました。しかし、2013年12月の規制緩和により、「ロボットメーカー、ユーザーが国際標準化機構(ISO)の定める産業用ロボットの規格に準じた措置を講じる」などの条件を満たせば、80W以上のロボットと人が同じ空間で働くことが可能になりました。この規制緩和により、国内で「協働ロボット」の開発が加速しています。

協働ロボットの登場により、今までロボット導入が難しかったところでも、人と協働してさまざまな作業ができるようになりました。

ちなみに、ロボ酒場のロボットも人間と溶け込み合う協働ロボットですね!

RaaS

SaaSとは「Software as a Service(サービスとしてのソフトウェア」の略です。僕たちソフトウェア業界では馴染み深い言葉です。

さて、RaaSをご存知でしょうか。「Robotics as a Service」の略です。ロボットも「所有」ではなく「利用」の時代になってきています。

pub.nikkan.co.jp

こちらの「機械設計2020年1月別冊 [雑誌:The ROBOT イノベーション×ビジネス]」に7つのサービスが掲載されています。

一番驚いたのは

自動収穫ロボットをRaaSモデルで提供し、農家の収入を倍増させる

の自動収穫ロボットサービスの記事でした。(ぜひ読んでみてください!)
このようなサービスがあれば、高齢化が進む農家でもリスクなく導入に踏み切ることができ、大いなる働き手になってくれるでしょう。新規就農者が増えることも期待されます。

ロボットの「手」の進化

ロボットは、「眼」(カメラやセンサー)と、見たものを認識・判断する「脳」(人工知能)の進化により、人間のように自律的に動くことができるようになってきました。しかし「手」についてはまだまだ進化の余地が残っています。

ロボットがモノを掴むためには、掴むモノに応じて最適なロボットハンドが必要となります。ロボットを生産ラインに組み込む際に毎回ロボットハンドを開発するとなると、時間的、金額的にもコストが大きくなってしまいます。そのため、人の手のような「高性能」でありながら「汎用性」を持つハンドが求められています。

2020年2月12日(水)~14日(金) 開催の「第4回 ロボデックス ~ロボット 開発・活用展~」でもロボットハンドの製品が多く出展するようです。

最後に

最近のロボット技術、活用方法などについてご紹介いたしました。いかがでしたでしょうか。

近い将来、家庭内にロボットがいるのが当たり前になったり、介護施設などでは人の相手をするロボットが普及するでしょう。2020年2月から成田空港第3ターミナルで警備ロボが巡回を始めます。人とロボットが協働して作り上げていく時代が、もう直ぐそこまで来ているのかもしれません。

株式会社ブリスウェル