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| +-----------+----------+-----------+----------+-------+---------+---+------------------+
参考文献
- サンプル集(group by詳しい)
- 関数まとめ
スライドの作りに便利なフリー素材サイト
はじめに
twitterでたまに回ってくる,スライド作成向け便利素材サイトをまとめました
画像系
freepik
- 高品質フリー素材画像サイト stories.freepik.com
Unsplash
pixabay
o-dan
図系
isoflow
- きれいなポンチ絵が掛けるサイト
- Really cool looking diagrams isoflow.io
スライド作り注意点
デザイン苦手な人がこれだけは避けるべき6選 pic.twitter.com/XifruFonum
— つだしん (@shin_5_9) 2020年6月24日
ubuntu18.04 noVNC dockerコンテナを公開しました
はじめに
この記事のSingularityコンテナを作成するべく,参照元のdockerコンテナを作成しました.
以下の素晴らしいリポジトリを使おうと思ったのですが,あいにくubuntu18.04に対応しておらず,リポジトリのメンテナンスも終わりそうです.
issueを漁ったところ,Fromをubuntu18.04にすれば動くらしいので,そこだけを変更したDockerfileGitHubに公開しました.
また,DokerHubでもコンテナを公開しました.
CentOSのアップデートバージョンを公開するかは悩み中です.(要望があれば行います.)
公開したもの
DockerHub
- https://hub.docker.com/r/tmyoda/ubuntu18-xfce-vnc
- https://hub.docker.com/r/tmyoda/ubuntu18-icewm-vnc
GitHub
SingularityコンテナでOpenCV+Boostを使ったC++コードをコンパイル
はじめに
研究でC++コードをpybind11を使ってPythonから呼んで強化学習をしています.
強化学習をGPU上で回したくなったのですが,残念ながらC++系ライブラリを導入するためのsudo権限が付与されておらず,サーバ上でC++コードが実行できません.
そこで,Singularityコンテナ上でビルド&実行までできる環境を作成して,そのままGPUを使った強化学習を行えるようにします.
コンテナ作成
作成したビルド&強化学習に必要な環境
実行方法は2つあります
- Singularity Libraryからpull
- .defファイルからローカルでbuild
Singularity Libraryからpull
Singularity Library(SyLabs)にコンテナを公開しました
以下のコードでビルドできると思います
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の詳しい使い方は以下の記事を参考にしてください
.defファイル
注意点
DockerHubで公開されている,
/consol/ubuntu-xfce-vnc
を使用したかったのですが,のubuntuバージョンが16.04までだったので,18.04に対応させたdocker container(tmyoda/ubuntu-xfce-vnc:18.04
)を作成し,DockerHubに上げたものをベースに作成しています.- 以下記事参照 tmyoda.hatenablog.com
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もMoJoCoもGUIです. というか,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の解説記事はこちらから
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のパネルが良く死ぬので初期化コマンド
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> |
また,先程作成したコンテナを元にいくつか実行例を示します.
- cuda torch検証用スクリプト
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
がある
参考
公式Document top page
公式Document Quick Start
公式Document Build
- 公式Document Instance
https://sylabs.io/guides/3.6/user-guide/cli/singularity_instance.html
- SingularityでDockerコンテナを使いたい
- [第2回 Singularity 勉強会] Singularity Hands-on
- SingularityコンテナをBuildする方法まとめ(Singularity v3.0.3)
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$をこえたあたりで足りなくなった.
そこで,この漸化式の行列表現を考える.
とすると以下のように表現できる.
このとき,$a_k$とは,文字列$S_k$に含まれるaの個数である.
この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乗法を使ったほうが良いと思います‥)
つぎに$[p, q]$だが,もし,$[p, q]$の範囲が,ある の文字数と同じ場合,先程の行列の結果をそのまま返せば良い. つまり,そのような$S_l$を探せば良い.
$S_k$は の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かつ複数の行動を出力するように変更したみたいです.しかし,この推薦システムが性能良いのか悪いのかについて議論されていないので,そこが気になりますね.
でも,この数百万オーダの空間へスケールアップと,バイアス・バリアンスへの対処は,強化学習の非常によい勉強になりました.
スライド中でも軽く方策勾配法について解説しているので,良かったら見てください.
参考にしたサイト
有志実装
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登録まで
参考記事に従って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 }}
で埋め込めます。
また、私の場合は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とかだと学習に違いが出るか気になります