クラウドエンジニアのノート

情報技術系全般,自分用メモを公開してます。

pysparkの使い方に慣れるためにirisデータセットをいじってみる

はじめに

pysparkを触る機会があったので,irisデータセットで色々試してみました. 適当にメモ程度なのであしからず.

環境構築

sparkの環境をローカルに構築するのが大変そうだったので,以下のDockerコンテナをつかいました.

https://hub.docker.com/r/jupyter/pyspark-notebook

サンプル集

読み込み

from pyspark import *
from pyspark.sql import *
from pyspark.sql.types import *
import pyspark.sql.functions as F


conf = SparkConf()
sc = SparkContext.getOrCreate(conf=conf)
sqlContext = SQLContext(sc)


df = sqlContext.read.format('com.databricks.spark.csv') \
    .options(header='true', inferschema='true') \
    .load('/home/jovyan/work/iris.csv') 

df.show()
+-----------+----------+-----------+----------+-------+
|sepalLength|sepalWidth|petalLength|petalWidth|variety|
+-----------+----------+-----------+----------+-------+
|        5.1|       3.5|        1.4|       0.2| Setosa|
|        4.9|       3.0|        1.4|       0.2| Setosa|
|        4.7|       3.2|        1.3|       0.2| Setosa|
|        4.6|       3.1|        1.5|       0.2| Setosa|
|        5.0|       3.6|        1.4|       0.2| Setosa|
|        5.4|       3.9|        1.7|       0.4| Setosa|
|        4.6|       3.4|        1.4|       0.3| Setosa|
|        5.0|       3.4|        1.5|       0.2| Setosa|
|        4.4|       2.9|        1.4|       0.2| Setosa|
|        4.9|       3.1|        1.5|       0.1| Setosa|
|        5.4|       3.7|        1.5|       0.2| Setosa|
|        4.8|       3.4|        1.6|       0.2| Setosa|
|        4.8|       3.0|        1.4|       0.1| Setosa|
|        4.3|       3.0|        1.1|       0.1| Setosa|
|        5.8|       4.0|        1.2|       0.2| Setosa|
|        5.7|       4.4|        1.5|       0.4| Setosa|
|        5.4|       3.9|        1.3|       0.4| Setosa|
|        5.1|       3.5|        1.4|       0.3| Setosa|
|        5.7|       3.8|        1.7|       0.3| Setosa|
|        5.1|       3.8|        1.5|       0.3| Setosa|
+-----------+----------+-----------+----------+-------+

カラム確認

df.columns
['sepalLength', 'sepalWidth', 'petalLength', 'petalWidth', 'variety']

統計量

df.describe().show()
+-------+------------------+-------------------+------------------+------------------+---------+
|summary|       sepalLength|         sepalWidth|       petalLength|        petalWidth|  variety|
+-------+------------------+-------------------+------------------+------------------+---------+
|  count|               150|                150|               150|               150|      150|
|   mean| 5.843333333333335|  3.057333333333334|3.7580000000000027| 1.199333333333334|     null|
| stddev|0.8280661279778637|0.43586628493669793|1.7652982332594662|0.7622376689603467|     null|
|    min|               4.3|                2.0|               1.0|               0.1|   Setosa|
|    max|               7.9|                4.4|               6.9|               2.5|Virginica|
+-------+------------------+-------------------+------------------+------------------+---------+

スライシング

df[['sepalLength', 'sepalWidth']]
DataFrame[sepalLength: double, sepalWidth: double]

ランダムサンプリング

df.sample(False, fraction=0.1).show()
+-----------+----------+-----------+----------+----------+
|sepalLength|sepalWidth|petalLength|petalWidth|   variety|
+-----------+----------+-----------+----------+----------+
|        4.9|       3.0|        1.4|       0.2|    Setosa|
|        5.7|       4.4|        1.5|       0.4|    Setosa|
|        5.2|       3.4|        1.4|       0.2|    Setosa|
|        4.7|       3.2|        1.6|       0.2|    Setosa|
|        6.3|       3.3|        4.7|       1.6|Versicolor|
|        6.0|       2.2|        4.0|       1.0|Versicolor|
|        6.1|       2.9|        4.7|       1.4|Versicolor|
|        5.6|       2.5|        3.9|       1.1|Versicolor|
|        5.5|       2.4|        3.8|       1.1|Versicolor|
|        6.0|       2.7|        5.1|       1.6|Versicolor|
|        6.7|       2.5|        5.8|       1.8| Virginica|
|        6.4|       2.7|        5.3|       1.9| Virginica|
|        7.4|       2.8|        6.1|       1.9| Virginica|
|        7.9|       3.8|        6.4|       2.0| Virginica|
|        6.7|       3.3|        5.7|       2.5| Virginica|
+-----------+----------+-----------+----------+----------+

列の追加

df = df.withColumn('PetalMult', df['petalWidth'] * df['petalLength'])
df.show(5)
+-----------+----------+-----------+----------+-------+-------------------+----+------------------+
|sepalLength|sepalWidth|petalLength|petalWidth|variety|          PetalMult|  ID|        totalWidth|
+-----------+----------+-----------+----------+-------+-------------------+----+------------------+
|        5.1|       3.5|        1.4|       0.2| Setosa|0.27999999999999997|40.0|0.7000000000000001|
|        4.9|       3.0|        1.4|       0.2| Setosa|0.27999999999999997|45.0|0.6000000000000001|
|        4.7|       3.2|        1.3|       0.2| Setosa|               0.26| 9.0|0.6400000000000001|
|        4.6|       3.1|        1.5|       0.2| Setosa|0.30000000000000004|79.0|0.6200000000000001|
|        5.0|       3.6|        1.4|       0.2| Setosa|0.27999999999999997|27.0|0.7200000000000001|
+-----------+----------+-----------+----------+-------+-------------------+----+------------------+

ユーザ定義関数

データ更新毎に呼ばれるので注意!!

my_udf = F.UserDefinedFunction(lambda x: x + 5, DoubleType())
df.withColumn("my_col", my_udf("sepalLength")).show(5)
+-----------+----------+-----------+----------+-------+-------------------+----+------------------+------+
|sepalLength|sepalWidth|petalLength|petalWidth|variety|          PetalMult|  ID|        totalWidth|my_col|
+-----------+----------+-----------+----------+-------+-------------------+----+------------------+------+
|        5.1|       3.5|        1.4|       0.2| Setosa|0.27999999999999997|40.0|0.7000000000000001|  10.1|
|        4.9|       3.0|        1.4|       0.2| Setosa|0.27999999999999997|45.0|0.6000000000000001|   9.9|
|        4.7|       3.2|        1.3|       0.2| Setosa|               0.26| 9.0|0.6400000000000001|   9.7|
|        4.6|       3.1|        1.5|       0.2| Setosa|0.30000000000000004|79.0|0.6200000000000001|   9.6|
|        5.0|       3.6|        1.4|       0.2| Setosa|0.27999999999999997|27.0|0.7200000000000001|  10.0|
+-----------+----------+-----------+----------+-------+-------------------+----+------------------+------+

グルーピング

  • 列の値でグループ分けし、一列の合計を取得する場合:
df.groupBy('variety').sum().show()
+----------+------------------+------------------+------------------+------------------+------------------+
|   variety|  sum(sepalLength)|   sum(sepalWidth)|  sum(petalLength)|   sum(petalWidth)|    sum(PetalMult)|
+----------+------------------+------------------+------------------+------------------+------------------+
| Virginica| 329.3999999999999|             148.7|277.59999999999997|101.29999999999998| 564.8099999999997|
|    Setosa|250.29999999999998|171.40000000000003| 73.10000000000001|12.299999999999995|18.280000000000012|
|Versicolor|             296.8|138.50000000000003|212.99999999999997|              66.3|            286.02|
+----------+------------------+------------------+------------------+------------------+------------------+
  • 列の値でグループ分けし、一列をカウントする場合:
df.groupBy('variety').count().show()
+----------+-----+
|   variety|count|
+----------+-----+
| Virginica|   50|
|    Setosa|   50|
|Versicolor|   50|
+----------+-----+

groupBy→aggで集計

  • filterは行の抽出
  • selectは列の抽出(スライシングで代用可能)
df.groupBy('variety').agg({'petalWidth': 'min', 'sepalWidth': 'min'}).filter('min(sepalWidth) > 2.0').show()
df.groupBy('variety').agg({'petalWidth': 'min', 'sepalWidth': 'min'}).filter('min(sepalWidth) > 2.0').show()
+---------+---------------+---------------+
|  variety|min(petalWidth)|min(sepalWidth)|
+---------+---------------+---------------+
|Virginica|            1.4|            2.2|
|   Setosa|            0.1|            2.3|
+---------+---------------+---------------+
df.groupBy('variety').agg(F.min('petalWidth')).show()
+----------+---------------+
|   variety|min(petalWidth)|
+----------+---------------+
| Virginica|            1.4|
|    Setosa|            0.1|
|Versicolor|            1.0|
+----------+---------------+

groupBy→pivotで縦横変換

# groupBy("縦のままの列").pivot("縦から横へ変換したい列").sum("集計値の列")
df.groupBy('ID').pivot('variety').sum('totalWidth').show()
+--------------------+------+------------------+------------------+
|                  ID|Setosa|        Versicolor|         Virginica|
+--------------------+------+------------------+------------------+
|4c86ce73cf93883ac...|  null|3.6399999999999997|              null|
|1d0123419d79ed9f4...|  null|              null|               5.6|
|9ee6f2276bb903d07...|  0.68|              null|              null|
|feb96d4b9ba61a234...|  0.66|              null|              null|
|9d1bf310e18c3906a...|  null|              null|              6.16|
|083c4bf137c021384...|  null|              null| 7.359999999999999|
|e8249fb5d09a3670e...|  null|              3.75|              null|
|d2a5cfc97fc88bd4a...|  null|              null|6.6000000000000005|
|cff32af7114873f26...|  null|              3.12|              null|
|78c2373ceaeedd5f7...|  null|              null| 5.319999999999999|
|c0299202f49017bf2...|  1.02|              null|              null|
|cf2b784ce8118783c...|  null|              null| 6.510000000000001|
|1b4ac9d118b902964...|  null|              null|              5.13|
|1aeacde745c81b265...|  null|               4.5|              null|
|b6f96e27904f4f6de...|  null|              null|               6.0|
|abed0bc37e718a5e4...|  null|              3.12|              null|
|b89932d736fa67711...|  null|              5.28|              null|
|7d132d0ead06d6ba5...|  null|              3.77|              null|
|cfb737bf348cd3b2c...|  null|3.9199999999999995|              null|
|42209ddc3b698bb94...|  0.68|              null|              null|
+--------------------+------+------------------+------------------+

一意の識別子を付ける

df = df.withColumn("ID", F.monotonically_increasing_id())
df = df.withColumn('totalWidth', df['sepalWidth'] * df['petalWidth'])
df.show(5)
+-----------+----------+-----------+----------+-------+-------------------+---+------------------+
|sepalLength|sepalWidth|petalLength|petalWidth|variety|          PetalMult| ID|        totalWidth|
+-----------+----------+-----------+----------+-------+-------------------+---+------------------+
|        5.1|       3.5|        1.4|       0.2| Setosa|0.27999999999999997|  0|0.7000000000000001|
|        4.9|       3.0|        1.4|       0.2| Setosa|0.27999999999999997|  1|0.6000000000000001|
|        4.7|       3.2|        1.3|       0.2| Setosa|               0.26|  2|0.6400000000000001|
|        4.6|       3.1|        1.5|       0.2| Setosa|0.30000000000000004|  3|0.6200000000000001|
|        5.0|       3.6|        1.4|       0.2| Setosa|0.27999999999999997|  4|0.7200000000000001|
+-----------+----------+-----------+----------+-------+-------------------+---+------------------+

DFのJOIN

df1 = df[['ID', 'totalWidth']].sort('ID')
print(df1.show(3), df1.count())
+---+------------------+
| ID|        totalWidth|
+---+------------------+
|  0|0.7000000000000001|
|  1|0.6000000000000001|
|  2|0.6400000000000001|
+---+------------------+
df2 = df[['ID', 'variety', 'PetalMult']].sort('ID')
print(df2.show(3), df2.count())
+---+-------+-------------------+
| ID|variety|          PetalMult|
+---+-------+-------------------+
|  0| Setosa|0.27999999999999997|
|  1| Setosa|0.27999999999999997|
|  2| Setosa|               0.26|
+---+-------+-------------------+
  • 元のDataframe(こちらがLeftになる)でjoin methodを呼び、joinの相手(Rightになる)とjoinの条件を書くと、SQLのjoinの様にDataframeの結合が可能
df1.join(df2, df1['ID'] == df2['ID'], 'inner').show()
+---+-------------------+---+-------+-------------------+
| ID|         totalWidth| ID|variety|          PetalMult|
+---+-------------------+---+-------+-------------------+
|  0| 0.7000000000000001|  0| Setosa|0.27999999999999997|
|  1| 0.6000000000000001|  1| Setosa|0.27999999999999997|
|  2| 0.6400000000000001|  2| Setosa|               0.26|
|  3| 0.6200000000000001|  3| Setosa|0.30000000000000004|
|  4| 0.7200000000000001|  4| Setosa|0.27999999999999997|
|  5|               1.56|  5| Setosa|               0.68|
|  6|               1.02|  6| Setosa|               0.42|
|  7|               0.68|  7| Setosa|0.30000000000000004|
|  8|               0.58|  8| Setosa|0.27999999999999997|
|  9|0.31000000000000005|  9| Setosa|0.15000000000000002|
| 10| 0.7400000000000001| 10| Setosa|0.30000000000000004|
| 11|               0.68| 11| Setosa|0.32000000000000006|
| 12|0.30000000000000004| 12| Setosa|0.13999999999999999|
| 13|0.30000000000000004| 13| Setosa|0.11000000000000001|
| 14|                0.8| 14| Setosa|               0.24|
| 15| 1.7600000000000002| 15| Setosa| 0.6000000000000001|
| 16|               1.56| 16| Setosa|               0.52|
| 17|               1.05| 17| Setosa|               0.42|
| 18|               1.14| 18| Setosa|               0.51|
| 19|               1.14| 19| Setosa|0.44999999999999996|
+---+-------------------+---+-------+-------------------+

列を取り出す

  • df.selectじゃないと動作しない
  • .rddとは
    • Dataframeの各行がそれぞれRow OjbectなRDDに変換されます。Row ObjectはSpark SQLで一行分のデータを保持する為のObjectです
sorted(df.select('ID').distinct().rdd.map(lambda x: x[0]).collect())[:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
df.filter(df['ID'] == 2).show()
+-----------+----------+-----------+----------+-------+---------+---+------------------+
|sepalLength|sepalWidth|petalLength|petalWidth|variety|PetalMult| ID|        totalWidth|
+-----------+----------+-----------+----------+-------+---------+---+------------------+
|        4.7|       3.2|        1.3|       0.2| Setosa|     0.26|  2|0.6400000000000001|
+-----------+----------+-----------+----------+-------+---------+---+------------------+

参考文献

sinhrks.hatenablog.com

  • サンプル集(group by詳しい)

qiita.com

  • 関数まとめ

qiita.com

スライドの作りに便利なフリー素材サイト

はじめに

twitterでたまに回ってくる,スライド作成向け便利素材サイトをまとめました

画像系

freepik

Unsplash

unsplash.com

pixabay

https://pixabay.com/ja/

o-dan

https://o-dan.net/ja/

図系

isoflow

スライド作り注意点

ubuntu18.04 noVNC dockerコンテナを公開しました

はじめに

tmyoda.hatenablog.com

この記事のSingularityコンテナを作成するべく,参照元のdockerコンテナを作成しました.

以下の素晴らしいリポジトリを使おうと思ったのですが,あいにくubuntu18.04に対応しておらず,リポジトリのメンテナンスも終わりそうです.

github.com

issueを漁ったところ,Fromをubuntu18.04にすれば動くらしいので,そこだけを変更したDockerfileGitHubに公開しました.

また,DokerHubでもコンテナを公開しました.

CentOSのアップデートバージョンを公開するかは悩み中です.(要望があれば行います.)

公開したもの

DockerHub

GitHub

SingularityコンテナでOpenCV+Boostを使ったC++コードをコンパイル

はじめに

研究でC++コードをpybind11を使ってPythonから呼んで強化学習をしています.

強化学習GPU上で回したくなったのですが,残念ながらC++系ライブラリを導入するためのsudo権限が付与されておらず,サーバ上でC++コードが実行できません.

そこで,Singularityコンテナ上でビルド&実行までできる環境を作成して,そのままGPUを使った強化学習を行えるようにします.

コンテナ作成

作成したビルド&強化学習に必要な環境

  • Ninja build
  • OpenCV 3.4.6
  • Boost 1.65
  • Pipenv
  • python
  • VLC(動画出力するため)

実行方法は2つあります

  • Singularity Libraryからpull
  • .defファイルからローカルでbuild

Singularity Libraryからpull

Singularity Library(SyLabs)にコンテナを公開しました

cloud.sylabs.io

以下のコードでビルドできると思います

singularity pull library://tmyoda/default/headless-ubuntu-xfce-pipenv-opencv-boost
singularity build --sandbox cpp_vnc headless-ubuntu-xfce-pipenv-opencv-boost.sif

実行方法は以下の通りです

singularity instance start -w cpp_vnc xfce
singularity start -w instance://xfce

Singularityの詳しい使い方は以下の記事を参考にしてください

tmyoda.hatenablog.com

.defファイル

注意点

  • DockerHubで公開されている,/consol/ubuntu-xfce-vnc を使用したかったのですが,のubuntuバージョンが16.04までだったので,18.04に対応させたdocker container(tmyoda/ubuntu-xfce-vnc:18.04)を作成し,DockerHubに上げたものをベースに作成しています.

  • OpenCVは動画生成したかったので,その辺のオプション有効にしてます

Bootstrap: docker
From: tmyoda/ubuntu18-xfce-vnc:latest

%environment
    export LC_ALL=C.UTF-8
    export Lang=C.UTF-8
    export PIPENV_VENV_IN_PROJECT=1
    export PATH="/usr/local/opt/openssl/bin:$PATH"
    # pipenv property
    export PIPENV_VENV_IN_PROJECT=1
    export PIPENV_SKIP_LOCK=1


%post
    apt update
    # for jupyter and pipenv 
    apt install -y bzip2 ca-certificates curl git ffmpeg openssl libssl-dev \
    libsqlite3-dev libreadline6-dev libbz2-dev libssl-dev libsqlite3-dev libncursesw5-dev \
    libffi-dev libdb-dev libexpat1-dev zlib1g-dev liblzma-dev libgdbm-dev libmpdec-dev \
    vim-tiny build-essential inkscape jed libsm6 libxext-dev libxrender1 lmodern netcat tzdata unzip \
    wget build-essential gcc zlib1g-dev python3-distutils vlc bash-completion

    # pip
    curl -kL https://bootstrap.pypa.io/get-pip.py | python3
    # pipenv
    pip3 install virtualenv
    pip3 install pipenv


    apt install -y \
        libboost1.65-all-dev libboost1.65-dev libboost1.65-tools-dev ninja-build ccache \
        libncurses5-dev libavutil-dev libavcodec-dev libavfilter-dev libavformat-dev libavdevice-dev ffmpeg pkg-config \
        cmake libgtk-3-dev libjpeg-dev

    # opencv3.4.6
    wget https://github.com/opencv/opencv/archive/3.4.6.tar.gz
    tar zxvf 3.4.6.tar.gz
    cd opencv-3.4.6

    export SOURCE_DIR="../"
    export BUILD_DIR="/build"
    export GENERATOR_NAME="Unix Makefiles"

    # build opencv
    mkdir build
    cd build
    cmake \
    -G "${GENERATOR_NAME}" \
    --build ${BUILD_DIR} \
    -D BUILD_CUDA_STUBS=OFF \
    -D BUILD_DOCS=OFF \
    -D BUILD_EXAMPLES=OFF \
    -D BUILD_JASPER=OFF \
    -D BUILD_JPEG=ON \
    -D BUILD_OPENEXR=OFF \
    -D BUILD_PACKAGE=ON \
    -D BUILD_PERF_TESTS=OFF \
    -D BUILD_PNG=ON \
    -D BUILD_SHARED_LIBS=ON \
    -D BUILD_TBB=OFF \
    -D BUILD_TESTS=OFF \
    -D BUILD_TIFF=OFF \
    -D BUILD_WITH_DEBUG_INFO=ON \
    -D BUILD_ZLIB=OFF \
    -D BUILD_WEBP=OFF \
    -D BUILD_opencv_apps=ON \
    -D BUILD_opencv_calib3d=ON \
    -D BUILD_opencv_core=ON \
    -D BUILD_opencv_cudaarithm=OFF \
    -D BUILD_opencv_cudabgsegm=OFF \
    -D BUILD_opencv_cudacodec=OFF \
    -D BUILD_opencv_cudafeatures2d=OFF \
    -D BUILD_opencv_cudafilters=OFF \
    -D BUILD_opencv_cudaimgproc=OFF \
    -D BUILD_opencv_cudalegacy=OFF \
    -D BUILD_opencv_cudaobjdetect=OFF \
    -D BUILD_opencv_cudaoptflow=OFF \
    -D BUILD_opencv_cudastereo=OFF \
    -D BUILD_opencv_cudawarping=OFF \
    -D BUILD_opencv_cudev=OFF \
    -D BUILD_opencv_features2d=ON \
    -D BUILD_opencv_flann=ON \
    -D BUILD_opencv_highgui=ON \
    -D BUILD_opencv_imgcodecs=ON \
    -D BUILD_opencv_imgproc=ON \
    -D BUILD_opencv_java=OFF \
    -D BUILD_opencv_ml=ON \
    -D BUILD_opencv_objdetect=ON \
    -D BUILD_opencv_photo=ON \
    -D BUILD_opencv_python2=OFF \
    -D BUILD_opencv_python3=ON \
    -D BUILD_opencv_shape=ON \
    -D BUILD_opencv_stitching=ON \
    -D BUILD_opencv_superres=ON \
    -D BUILD_opencv_ts=ON \
    -D BUILD_opencv_video=ON \
    -D BUILD_opencv_videoio=ON \
    -D BUILD_opencv_videostab=ON \
    -D BUILD_opencv_viz=OFF \
    -D BUILD_opencv_world=OFF \
    -D CMAKE_BUILD_TYPE=RELEASE \
    -D WITH_1394=ON \
    -D WITH_CUBLAS=OFF \
    -D WITH_CUDA=OFF \
    -D WITH_CUFFT=OFF \
    -D WITH_EIGEN=ON \
    -D WITH_FFMPEG=ON \
    -D WITH_GDAL=OFF \
    -D WITH_GPHOTO2=OFF \
    -D WITH_GIGEAPI=ON \
    -D WITH_GSTREAMER=ON \
    -D WITH_GTK=ON \
    -D WITH_INTELPERC=OFF \
    -D WITH_IPP=ON \
    -D WITH_IPP_A=OFF \
    -D WITH_JASPER=ON \
    -D WITH_JPEG=ON \
    -D WITH_LIBV4L=ON \
    -D WITH_OPENCL=ON \
    -D WITH_OPENCLAMDBLAS=OFF \
    -D WITH_OPENCLAMDFFT=OFF \
    -D WITH_OPENCL_SVM=OFF \
    -D WITH_OPENEXR=ON \
    -D WITH_OPENGL=ON \
    -D WITH_OPENMP=OFF \
    -D WITH_OPENNI=OFF \
    -D WITH_PNG=ON \
    -D WITH_PTHREADS_PF=OFF \
    -D WITH_PVAPI=ON \
    -D WITH_QT=OFF \
    -D WITH_TBB=ON \
    -D WITH_TIFF=ON \
    -D WITH_UNICAP=OFF \
    -D WITH_V4L=OFF \
    -D WITH_VTK=OFF \
    -D WITH_WEBP=ON \
    -D WITH_XIMEA=OFF \
    -D WITH_XINE=OFF \
    ${SOURCE_DIR}

    make -j8
    make install
    echo /usr/local/lib > /etc/ld.so.conf.d/opencv.conf
    ldconfig -v

    # clean up
    apt clean
    rm -rf /var/lib/apt/lists/*

%labels
    Author tmyoda
    Version v0.0.3

Singularity + headless VNC + Pipenvを使ってサーバ上で強化学習環境を整える(gym, pybullet)

はじめに

強化学習をしてると何かとGUI問題に直面します. OpenAI GymもPyBulletもMoJoCoGUIです. というか,GUIで動作確認ぐらいしか,学習経過を把握できるものがありません.

ということで,GPUサーバ上でGUIが見れるような環境が欲しくなりました.

追記: Sylabs.ioにcontainerをpushしました.

headless VNCとは

VNCとは離れた環境のGUIを操作するリモートデスクトップのことを指しますが,headlessマシンとはディスプレイを持たない,つまりGUIを持たないマシンのことを指します.

つまり,GUIを持たない共有のGPU付きサーバ上に,xfce環境 (GUI) を作って,しかもブラウザ上からVNCできかつ,pytorchがGPU上で動く環境を作成します.

headless VNCを触ってみる

楽するために,Docker, もしくはSingularityを使って今回は進めます. また,私はxfceの方が好きなのでxfceでの例を示します.(もちろんIceWMでも良いです)

Singularityの解説記事はこちらから

tmyoda.hatenablog.com

https://hub.docker.com/r/consol/ubuntu-xfce-vnc/

docker

docker run -d -p 5901:5901 -p 6901:6901 consol/centos-xfce-vnc:latest

Singularity

# sandbox作成
singularity build --sandbox ubuntu-xfce docker://consol/ubuntu-xfce-vnc:latest

# instance作成 
singularity instance start -w --nv ubuntu-xfce xfce

# vnc実行
singularity run instance://xfce

singularityでそのままrunしても動きますが,プロセス管理が大変なのでinstanceで動かします.

あとはブラウザでlocalhost:6901/vnc.htmlへアクセスすればなんとGUIが見れます.(初期パスワードは vncpassword ) これでgymとかpybulletとかの動作を確認できます.

導入

docker

dockerの導入はかんたんで,先程のコンテナに好きなだけaptでほしいものを入れればokです.

singularity (rootless)

ルート権限サーバへの導入方法は3つあります. - singularity libraryからpull - ローカルPCでbuild - サーバ上でremote build

singularity libraryからpull (おすすめ!)

sylabにuploadしました.

singularity pull library://tmyoda/default/headless-ubuntu-xfce-pipenv

これで.sifファイルが手に入ります.

ローカルPCでbuild

rootlessだとaptコマンドが実行できないため,ローカルPCにsingularityを導入しました. そして,.defファイルを作成しましたので公開します.

Bootstrap: docker
From: consol/ubuntu-xfce-vnc:latest


%environment
    export LC_ALL=C.UTF-8
    export Lang=C.UTF-8
    export PIPENV_VENV_IN_PROJECT=1
    export PATH="/usr/local/opt/openssl/bin:$PATH"


%post
    apt-get -y update 
    # for jupyter and pipenv 
    apt-get install -y bzip2 ca-certificates curl git ffmpeg openssl libssl-dev \
    libsqlite3-dev libreadline6-dev libbz2-dev libssl-dev libsqlite3-dev libncursesw5-dev \
    libffi-dev libdb-dev libexpat1-dev zlib1g-dev liblzma-dev libgdbm-dev libmpdec-dev \
    vim-tiny build-essential inkscape jed libsm6 libxext-dev libxrender1 lmodern netcat tzdata unzip
    
    # pyton3.6 from source
    apt-get install -y wget build-essential gcc zlib1g-dev
    cd /root/
    wget https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tgz
    tar zxf Python-3.6.8.tgz
    cd Python-3.6.8
    ./configure 
    make altinstall
    cd .. && rm -rf Python-3.6.8.tgz Python-3.6.8
    cd $HOME
    rm /usr/bin/python3
    ln -s /usr/local/bin/python3.6 /usr/bin/python3
    # install pip
    export PATH="/usr/local/opt/openssl/bin:$PATH"
    curl -kL https://bootstrap.pypa.io/get-pip.py | python3
    apt-get clean
    rm -rf /var/lib/apt/lists/*
    # pipenv
    pip3 install virtualenv
    pip3 install pipenv

%labels
    Author tmyoda
    Version v0.1.0

root権限付きローカルPCでbuildします.

sudo singularity build --fix-perms ubuntu-xfce-pipenv.sif ubuntu-xfce-pipenv.def

*.defからローカルでbuildするのはroot必須です. しかしsingularityには--remoteというオプションがあり,これを用いると非rootでもbuildできます.

出来上がったxfce-pipenv.sifをscp等でサーバへコピーします.

サーバ上での作業

pullもしくはdefからのbuildでサーバ上に*.sifが手に入っている状態かと思います.

1 サーバ上で--sandboxオプションをつけてビルドします. これしないとvnc動かないです.

singularity build --sandbox ubuntu-xfce ubuntu-xfce-pipenv.sif

2 サーバ上でinstance startしてrunします.

singularity instance start -w --nv  ubuntu-xfce xfce
singularity run -w --nv instance://xfce

あとは先程のようにlocalhost:6901/vnc/htmlにアクセスします.

そして,pipenvが導入されているので,gym, pybullet, jupyterなりを好きに入れ環境を整えます.

xfce4 パネル初期化コマンド

最後にxfce4のパネルが良く死ぬので初期化コマンド

www.achiachi.net

xfce4-panel --quit
pkill xfconfd
rm -rf ~/.config/xfce4/panel
rm -rf ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml
xfce4-panel

singularityの使い方

はじめに

singularity v3.6.0時点での記事です

メリット

  • SingularityとはDockerと同じコンテナプラットフォームで,主にHPC向けに作られたオープンソースソフトウェア
  • Dockerより簡単にGPUを使うことができるらしい (最近はnvidia-dockerがdockerに統合されたため,このメリットは微妙)
  • Docker互換(Dockerfile, Docker imageを持ってくることができる)
  • rootlessでも動作
  • 自動で以下の領域をマウント(sudo付けないと一部マウントされないかも)
host singulalrity
$HOME $HOME
/sys /sys
/proc /proc
/tmp /tmp
/var/tmp /var/tmp
/etc/resolv.conf /etc/resolv.conf
/etc/passwd /etc/passwd
$PWD $PWD

Dockerとの違い

SingularityのアーキテクチャはDockerと少し異なります. Singularityのコンテナは

  • immutableな*.sif(Singularity Image File)
  • muutableなsandbox (フォルダ)

の2種類あります. Dockerのコンテナは変更可能なので,singularityで言うsandboxに該当します.

$HOMEなどが自動マウントされるので,基本的はsifを使うみたいですが,sifを使ったコンテナは後々pipでライブラリを追加したり出来ません.
Dockerコンテナライクな使い方をしたい場合や,環境を作って行きたい場合はsandboxを使うことをオススメします.

sandboxである程度環境が整った後に.sifに変換することも可能です. - sandboxで扱った後にimuutableにする方法

singularity build some-image.sif sandbox-dir

コンテナ作成方法

  • Docker Hub,Singularity Hub, Container Libraryからビルド
  • マシン上の既存コンテナからビルド(Docker Imageも可)
  • Singularity定義ファイル*.defからビルド

の3種類の方法があります.

buildコマンドを使用してコンテナを作成します.

Usage: singularity build [local options...] <IMAGE PATH> <BUILD SPEC>

コンテナ作成例

pytorch/conda-cudaをDocker Hubから.sif作成

  • conda-cuda-torch.sifという名前の.sifファイルを作成する例
singularity build conda-cuda-torch.sif docker://nablascom/cuda-pytorch:latest

sandboxでコンテナを作成するオプション

  • --sandboxオプションをつけてIMAGE PATH を任意のフォルダ名(ここではconda-cuda-torch)にすればOK
singularity build --sandbox conda-cuda-torch docker://nablascom/cuda-pytorch:latest

マシン上のdockerにあるhello-world imageから

singularity build hello-world-singularity.sif docker-daemon://hello-world:latest

conda-torch.defから

  • Dockerfileに相当するSingularity Definition File(.def)からビルドする例
  • sudoが必要
sudo singularity build conda-torch.sif conda-torch.def

コンテナ実行方法

実行コマンドは以下の3つあります.

この記事から引用すると

cmd 説明 Usage
exec ホストシステム上で直接実行されているかのように、
コンテナイメージ内の任意のコマンドを実行できます。
singularity exec [exec options...] <container> <command>
shell コンテナ内に対話型シェルを自動的に生成します。 singularity shell [shell options...] <container>
run コンテナがファイル名で直接実行または実行されたときに
実行されるカスタムアクションを定義できます。
singularity run [run options...] <container>

また,先程作成したコンテナを元にいくつか実行例を示します.

import torch
dev = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(dev)

conda-cuda-torch.sifの環境上でpythonスクリプトを実行

  • sandboxの場合は.sifの部分に対応するフォルダ名を指定すれば良い
singularity exec conda-cuda-torch.sif python check_torch_cuda.py
$ cpu

GPU環境でpythonスクリプト実行

--nvオプションをつける

singularity exec --nv conda-cuda-torch.sif python check_torch_cuda.py
$ cuda:0

shellに入る

singularity shell --nv  conda-cuda-torch.sif
$ Singularity> 

sandbox環境に書き込み可能な状態でshell実行(Dockerのコンテナはこの状態)

  • デフォルトは読み取り専用なので、--writableを付ける
singularity shell --nv --writable conda-cuda-torch
$ Singularity> 

run補足

  • sif作成時,%runscriptを指定することで,singularity run file_name.sifもしくは./file_name.sifのように実行形式ファイル風に実行できる

port forwardingについて

jupyterを使いたい場合があると思いますが,Singularityはデフォルトで多くの領域をマウントするので,勝手にhostのポートを使ってくれます.(webカメラ等も同様)

DBやWebサーバ等をバックグラウンドで実行したい時

Dockerライクなインターフェイスとして,singularity instanceコマンドがあります.

詳しくはこちらのDocumentへ - https://sylabs.io/guides/3.6/user-guide/cli/singularity_instance.html

singularity instanceでバックグラウンド実行する方法

  • 一応jupyter notebookをバックグラウンドで実行して停止する例を一つ出しておきます.
  • もちろんDockerのようなポートフォワーディングの記述は不要です.
# cuda-torchは INSTANCE NAME
singularity instance start --nv conda-cuda-torch.sif cuda-torch

# docker ps的なコマンド
singularity instance list
$ INSTANCE NAME    PID      IP    IMAGE
$ cuda-torch       14030          /home/oda/conda-cuda-torch.sif

# shell起動
singularity shell instance://cuda-torch
Singularity> jupyter notebook
...
# exitで抜ける
Singularity> exit

# インスタンスを停止
singularity instance stop cuda-torch

# インスタンスがないことを確認
singularity instance list
$ INSTANCE NAME    PID    IP    IMAGE

まとめ

  • Docker互換
  • コンテナはimmutableな.sifとsandboxがある
  • build時に--sandboxを指定しかつ,実行時に--writableを指定すると書き込み可能
  • 実行はexec, shell, runがある
  • 実行時,勝手にマウント
  • --nvコマンドだけでGPU対応
  • バックグラウンドで動かしたいときDockerライクなコマンドinstanceがある

参考

PFN2020インターンの課題 解答

PFN2020インターンに応募した話

M1になり,そろそろ夏インターンを探さなきゃってことで,色々応募しました.

強化学習インターンをやりたかったのですが,勉強不足を面接であっさり見抜かれてしまい,落ちてしまいました.

これを糧に,これから強化学習の勉強をしっかりしようと強く思いました.

現在はberkeleyの強化学習スライドをベースに,基礎的な部分の勉強をしています.いずれはそれをまとめた記事を公開しようと思ってます.

PFN2020課題

Github上で2020の課題が公開されたので大丈夫だとは思いますが,もし何か差し支えるようでしたら,ご連絡頂けますと幸いです.

こちらのリポジトリに解答例を載せましたので,ソースはこちらをご覧ください. github.com

また,問題文はこちらです github.com

PFNの課題というと,深層学習のイメージがありましたが,今回はかなり競プロっぽい内容でした. 最近はそういう流れなんですかね.

また,大問が3つあって,それぞれコーディング課題と,その正しさを検証する課題の2つセットで解答する必要があります.

もし数式が正しく表示されていなければ,ブラウザをリロードしてみてください.

Q1

3つの整数 x, y, d が与えられる。 3x3 行列の各要素を x または y で埋めるとき、行列式がちょうど d になるような 埋め方は何通りあるか数えよ。

問題の解法説明

まず最初に思いつくアプローチは全探索である.

しかし,3x3行列というのは各列を3次元ベクトルと見ると,平行六面体を表しており,行列式の値はその体積に相当する.つまり同じ向きのベクトルが存在すると体積は0になる.この性質を利用して計算量を減らした.

$d \neq 0$ の場合

  • 3次元ベクトルそれぞれが取ることができるx, yの組み合わせは$2^{3}=8$通り
  • そこから重複なく3つ選ぶ数は ${}_8 C_3 = 56$ 通り
  • また,ベクトルをそれぞれ入れ替えて行列式の符号が同じになるような組み合わせは3通り

つまり56通り検索し,見つかった数×3すれば良い.

$d == 0$の場合

  • 同じく56通り調べる(転置してベクトルが0になる場合)
  • 重複する選び方の数(=全通り - 重複しない数)を計算

行列式が0なので,見つかった数×6に重複する選び方の数を足せば良い.

プログラムの正しさの検証

q1_in.txt を総当りのプログラムとの出力の比較を行った.

境界条件としてdが正・負・0, x, yが同符号・異符号・0の場合のすべての組み合わせ(12通り)について検証を行った.

また,おまけとして,全通りでも制約すべての条件下で現実的な時間内に終わりそうだったため,q1_validation_extra.pyにてすべての場合の検証を行った.

進捗を見やすくするため,tqdmという外部ライブラリを使用した.(並列化してますが,それでも手元の6コアCPUで30分程度かかりました)

Q2

文字列 S1, S2, ... を以下で定める。

  • S1 = "a"
  • S2 = "b"
  • S3 = "c"

Sk = Sk-3 + Sk-2 + Sk-1 (k ≥ 4) (ここで、+ は文字列の結合を表す) 例えば、S4 = "abc", S5 = "bcabc" である。 3つの整数 k, p, q が与えられる。 Sk の p 文字目から q 文字目までに含まれる文字 "a", "b", "c" の個数をそれぞれ求 めよ。

問題の解法説明

まず最初に思いつくアプローチとしてDPがある.しかし試してみたところ,メモリが$k > 40$をこえたあたりで足りなくなった.

そこで,この漸化式の行列表現を考える.

 Svec_{k} = \begin{pmatrix}a_k&b_k&c_k&sum_k \end{pmatrix}

とすると以下のように表現できる.

このとき,$a_k$とは,文字列$S_k$に含まれるaの個数である.

 
\begin{pmatrix}Svec_k\cr Svec_{k-1}\cr Svec_{k-2}\end{pmatrix}=A^{k-3}\begin{pmatrix}0&0&1&1\cr 0&1&0&1\cr 1&0&0&1\end{pmatrix}, (k>3)


このAを毎回累乗して求めても良いが,高速化のためにこのAを対角化してn乗を求めておく. すると$O(1)$で,文字列$S_k$に含まれるa, b, c の個数がわかる.

また,この行列は固有値虚数を含むため誤差が生じる.しかし,$A^{50}$のときの誤差が $ [0.0144 +0.0016j, 0.0124+0.0014j, 0.0072+0.0013j] $ であるため,問題ないと言える.

(当時は2乗法を知らずn次行列を求めてますが,あまり美しい方法じゃないので,2乗法を使ったほうが良いと思います‥)

qiita.com

つぎに$[p, q]$だが,もし,$[p, q]$の範囲が,ある S_l, (l  \leq k) の文字数と同じ場合,先程の行列の結果をそのまま返せば良い. つまり,そのような$S_l$を探せば良い.

$S_k$は S _ {k-3}+S _ {k-2}+S _ {k-1} の3つの文字列から構成されている. つまり,$[p, q]$は$S_k$の各構成要素を跨ぐ場合と,そうでない場合に分けることができる.

以上の性質を用いると以下のようなアルゴリズムを考えることができる.

区間が跨いでいる場合

  • それぞれを区間を跨がないように分割し,最後にそれぞれの結果を足す

区間が跨いでいない場合

  • $[p, q] = S_{l}$ ならば,行列計算の結果を返す
  • $[p, q] \neq S_l$ ならば,対応する部分の$S_m, (m < l)$を調べる

以上の手順を再帰的に繰り返すことによって調べることができる.

プログラムの正しさの検証

プログラムの境界条件と,それぞれが1回以上の文字連結で構成されている$k=7$でのすべてのケースについて検証を行った.
また,DPで解くプログラムの限界が$k<40$であるので,その条件に満たすq2_in.txt でも同様に出力の検証を行った.

Q3

長いので省略

問題の解法説明

まず考えられるアプローチとして,各距離をすべて保持する方法が考えられる.

単純に,ある$j$番目人を最も距離が大きいかつ一番左の$a_j$に座らせて,$a_j$から両端まで距離を更新して行けば良い.

しかし,この手法は$O(n^{2})$であり,制約の$n$が大きいため時間がかかる.そのため高速化を考える.

ある$j$番目の人が$a_j$に座ったとき,そこから$l$方向($0 < l < j$)と, $r$方向の区間($j < r \leq n$)のまだ誰も座っていない区間に分割する.

それぞれ分割された区間の中で,区間の左右から最も離れた位置が最も大きい場所に座れば良い.
つまり,分割された区間が$l$から$r$とすると($l$は0から),その区間内で離れることができる最大の距離$dist$は$(l - r) // 2$であり, 座る位置のindexは$l + dist$として表現できる.また,$dist$が最も大きいものから(同じからlが小さい方)から選んでいけば良い.

この手順を再帰的に繰り返し適用していけば,すべての座る位置が求まる.
しかし,最初の分割だけ例外があり,端に誰も座っていないため2で割る必要がない.よって$dist = l - r$とした.

実装上の工夫として,$dist$の最も大きい値を効率的に探すためにheapqを用いた.

プログラムの正しさの検証

プログラムの境界条件(最初に端に座る,n=1など)の検証を行った.
また,$O(n^{2})$の方法が現実的な時間で終わるのが$k < 10000$なので,その条件を満たすq3_in.txt についても同様の検証を行った.

Top-K Off-Policy Correction for a REINFORCE Recommender System

スライド

説明

Top-K Off-Policy Correction for a REINFORCE Recommender System – Google Research

YouTubeで実際に運用された(今も運用されてるかは不明)強化学習を用いた推薦システムの論文です.

内容として,RENFORCEをoff-policyかつ複数の行動を出力するように変更したみたいです.しかし,この推薦システムが性能良いのか悪いのかについて議論されていないので,そこが気になりますね.

でも,この数百万オーダの空間へスケールアップと,バイアス・バリアンスへの対処は,強化学習の非常によい勉強になりました.

スライド中でも軽く方策勾配法について解説しているので,良かったら見てください.

参考にしたサイト

medium.com

qiita.com

有志実装

実装主のブログ
https://towardsdatascience.com/top-k-off-policy-correction-for-a-reinforce-recommender-system-e34381dceef8

Github github.com

Azure Web App + Flask + Github Actionsで認証ページ付きポートフォリオをデプロイする方法

はじめに

以前Firebaseでデプロイしたときをベースに書いてますので、先にこちらを見て下さい。 tmyoda.hatenablog.com

Azure クラウド サービス | Microsoft Azure にはとんでもない数のサービスがありますが 、 今回はポートフォリオを公開するために、WebのApp Serviceを使用しました。分類的にはPaaSですかね。

そしてなにより、1Gメモリ F1を選択すると無料で使えます。 また、学生でしたら、登録したときに1万円分のサブスククーポンを貰えます。

概要

  • AzureでWebページをデプロイ
  • AzureではBasic認証が使えないので、ログインページを用意(Flask)
  • 以前Firebaseでデプロイしたhtml,css,jsを使いまわしたいのでサブモジュールにて保持

参考にさせて頂いた記事

  • Azureの登録方法等 qiita.com

  • Flaskでログイン機能の実装 qiita.com

  • Bootstrap Login form

www.tutorialrepublic.com

Azure登録まで

参考記事に従ってAzureを登録します。 このとき、Flaskを使いたいのでランタイムスタックをPython3にします。

しばらくするとインスタンスが作成されます。

Flaskで認証ページを実装

Firebase Hostingでデプロイしたときは、Basic認証が使えたので、実質静的ページの公開と同じでした。

しかし、AzureはBasic認証が使えないようなので、Flaskでかんたんなログインを実装しようと考えました。 参考記事に従いながら実装していると‥

なんと、Flaskでhtmlを表示するにはjinja2のテンプレートに対応した記述が必要です。

しかし、サブモジュールでhtmlを保持しているため、書き換え等はしたくない、、、

かといってFlaskのstaticフォルダに格納したら直接が見られるが認証がかけられない、、、

そこで、

  • @app.route('/portfolio/')にGETが来たら、直接htmlをテキストとして読み出しResponse型で返す
  • その他のcss,jsへのアクセスが来たら、send_fileする

といった方針でひとまずやりたいことは実現しました。

解決策

from flask import Flask, request, Response, abort, render_template, redirect, url_for, send_file
from flask_login import LoginManager, login_user, logout_user, login_required, UserMixin
from collections import defaultdict
import os
import logging

app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
app.config['SECRET_KEY'] = なんらかしらのキーを設定

# logging setting
logging.basicConfig(level=logging.DEBUG)
#logging ex
# app.logger.info("Hello World %s", variable) 


# path setting
currnet_dir = os.path.dirname( os.path.abspath(__file__) )
static_dir = 'portfolio/functions/static/'


class User(UserMixin):
    def __init__(self, id, name, password):
        self.id = id
        self.name = name
        self.password = password

# ログイン用ユーザー作成
users = {
    1: User(1, "user01", "password"),
    2: User(2, "user02", "password")
}

# ユーザーチェックに使用する辞書作成
nested_dict = lambda: defaultdict(nested_dict)
user_check = nested_dict()
for i in users.values():
    user_check[i.name]["password"] = i.password
    user_check[i.name]["id"] = i.id

@login_manager.user_loader
def load_user(user_id):
    return users.get(int(user_id))


# css, js ,imgなどのコンポーネント呼び出し用
@app.route('/portfolio/<path:path>/<string:filename>', methods=["GET"])
@login_required
def portfolio_content(path, filename):
    return send_file(static_dir + path + "/" + filename)    

# index.html呼び出し 
# .htmlはこの方法じゃないとだめみたい
@app.route('/portfolio/')
@login_required
def portfolio():
    with open(currnet_dir + '/' + static_dir + 'index.html') as f:
        txt = f.read()
        return Response(txt)


# ログインパス
@app.route('/', methods=["GET", "POST"])
def login():
    if(request.method == "POST"):
        # ユーザーチェック
        if(request.form["username"] in user_check and request.form["password"] == user_check[request.form["username"]]["password"]):
            # ユーザーが存在した場合はログイン
            login_user(users.get(user_check[request.form["username"]]["id"]))
            return redirect(url_for('portfolio'))
        else:
            return abort(401)
    else:
        return render_template("login.html")

# ログアウトパス
@app.route('/logout/')
@login_required
def logout():
    logout_user()
    return Response('''
    logout success!<br />
    <a href="/login/">login</a>
    ''')

if __name__ == '__main__':
    app.run(threaded=True)

pass,idベタ書きしてますが、本当はDBに格納してパスワードもハッシュ化しないといけませんね。 まあ最悪全世界に晒されても良い内容なので、今回はこの程度で良いでしょう。

/portfolio に来るアクセスはindex.htmlを返して、 /portfolio/img/, css/ とかはsend_fileで返却します。

フォルダ階層

.
├── Pipfile
├── Pipfile.lock
├── app.py
├── portfolio
│   └── functions
│       └── static
│           ├── css
│           ├── img
│           ├── index.html
│           ├── js
│           └── vendor
├── requirements.txt
└── templates
    └── login.html

この工夫点として、portfolioフォルダは別リポジトリのサブモジュールです。 なので、どっちかでポートフォリオをアップデートしたら、もう片方がアップデートされます。

また、 staticフォルダを作成していない点もポイントです。(staticは外から見える)

そして、Pythonは、*.py、requirements.txt、または runtime.txtが最初に呼び出されるらしいので、 名前をちゃんと変えておきましょう。

デプロイ

ここまできたらいよいよです。今回はGithub Actionsでのデプロイが目標ですので、Azure portalのデプロイメントから、 Githubを選択して、Actionsを選択します。(要認証)

すると勝手にworkflowが作成されるので、次々と進むと勝手にCI/CDが走ってデプロイ完了です。 非常にかんたんですね!

注意点として、再起動しないと認識されないので注意です。

サブモジュール関連のトラブル

サブモジュール使ってない人はパスして下さい。

サブモジュールがpullされておらず、htmlがNot Foundになりました。 その解決方法として、追記したworkflow/ymlを晒します。

    steps:
    - uses: actions/checkout@master
      with:
        submodules: true
        token: ${{ secrets.PORTFOLIO_ACCESS_TOKEN }}
    
    - name: Sparse-Checkout
      run: |
        echo /functions/static >> .git/modules/portfolio/info/sparse-checkout
        cd portfolio
        git config core.sparsecheckout true
        git read-tree -mu HEAD
        git checkout master
        cd ..

まず、プライベートリポジトリでしたので、アクセストークンを取得して設定する必要があります。 以下を参考に作成して、サクッと設定しましょう。見ての通り、token: ${{ secrets.PORTFOLIO_ACCESS_TOKEN }}で埋め込めます。

help.github.com

また、私の場合はhtmlに関係するファイルのみ欲しかったので、sparse-checkoutしています。 一括でうまくやってくれるworkflowファイルを見つけられなかったので、runでベタ書きしました。

とりあえず、これで上手く動いてくれているので良かったです。 (すぐデプロイするつもりがなんだかんだ一日かかっちゃいました‥)

注意点

  • 再起動しないとデプロイが反映されない
  • privateリポジトリをサブモジュールで持つなら、Access Tokenが必要
  • FlaskはStatic以下は常に晒されている
  • Secretkey pass, idなどはちゃんとDB管理

追記

Azureは適当にhtmlファイル置くだけでも表示されるらしいので、なんかのWebサーバが裏で動いているんですかね、わかりません。

そしてなんと、Azure Static Web Appというより気軽なサービスが最近追加されたらしいです‥ こっち使ったほうが早かったですね

Learning agile and dynamic motor skills for legged robots 解説スライド

スライド

説明

  • 複雑なモータ制御が必要なロボットの制御方法を提案
  • シミュレーションのみで学習した方策をロボットに転送し、実 環境のロボット制御に成功
  • ロボットのシミュレーションとの違いをNNによって吸収
  • これによりシミュレータでのモデリングが改善
  • 方策はシミュレーション上のみで学習
  • 既存のSOTAのモデルベース手法より優れた性能
  • より、少ないエネルギー、計算量ながら、より高速で高い精度
  • 本論文は多脚ロボットの汎用的なコントローラの獲得への一歩

感想

actuator netの精度を向上させたら、ゴリゴリのコスト関数設計はもう少し楽になるのでしょうか‥

恐らくノイズを載せまくってるのがコスト関数を複雑化させている原因になっているので、もう少しシミュレーションが 現実に近いと楽なんですかね
また、TRPOじゃなくてPPOとかSACとかだと学習に違いが出るか気になります