AWSのインスタンスをSDKを使ってローカルからいい感じにする話
こんにちは. 趣味でGPUインスタンスを使うことがあったりなかったりするのですが とても不定期なので都度起動,停止をするのがめんどくさく,RakeTaskを書きました.
コード
require 'aws-sdk' class SetupSSH class << self @@client = Aws::EC2::Client.new(access_key_id: ENV['GPU_AWS_ACCESS_ID'], secret_access_key: ENV['GPU_AWS_ACCESS_SECRET']) def fetch ids = fetch_instances(state: 'stopped').map(&:instance_id) @@client.start_instances(instance_ids: ids) @@client.wait_until(:instance_running, instance_ids: ids) config = fetch_instances(state: 'running').map(&:public_ip_address) .map { |ip_address| config_for_instance(ip_address) } .flatten .join("\n") target_dir = "#{ENV['HOME']}/.ssh/conf.d" FileUtils.mkdir_p(target_dir) File.write("#{target_dir}/config_essay", config) p config end def stop ids = fetch_instances(state: 'running').map(&:instance_id) @@client.stop_instances(instance_ids: ids) begin @@client.wait_until(:instance_stopped, instance_ids: ids) do |w| w.interval = 15 w.max_attempts = 20 end p 'instances have stopped' rescue Aws::Waiters::Errors::WaiterFailed => error p "failed waiting for stopping instance: #{error.message}" end end private def fetch_instances(state:) @@client .describe_instances .reservations .map { |r| r.instances.select { |i| i.state.name == state } } .flatten end def config_for_instance(ip_address) <<~EOS Host gpu-#{ip_address} HostName #{ip_address} User ubuntu LocalForward 8888 localhost:8888 IdentityFile ~/.ssh/make/gpu.pem EOS end end end
基本的な流れは 停止中のインスタンスのidをfetch→起動→waitしてconfigに吐き出すというという流れです (config中でconf.d以下のfileをincludeしていること前提です) pecoとか使っておくと複数個のインスタンスを管理しておくのに便利です。
ついでにローカルの8888をポートフォワーディングとかしておくと jupyterNoteBookとかを起動した時に何も考えずlocalの8888に繋げば良いだけなので便利だと思います. AWS上で運用するならcloudWatchとかでCPUUsageベースでシャットダウンでも良い気がしますが メトリクスを決めるのがめんどいので一応手動で切れるようにしてます.
Deep Count: Fruit Counting Based on Deep Simulated Learning の プログラムとか
Deep Count: Fruit Counting Based on Deep Simulated Learning
この前の記事で読んだとかいってた論文を見ながら実際にやってみて、精度でるんか? みたいなものを一旦出したいとのことなので手を動かしてみた(pythonクソ初心者です)
環境
python 3.6.3
やったこと
花実の数を数えるにあたり、データセット(画像+写ってる花実の数)を用意するのがとりあえずめんどいから、synthetic imagesで代用したよ〜との記述があったのですが、
そのプログラムとか一ミリもなかったので、とりあえずpython練習がてらプログラムを書きました
jupyter notebookとかで実行するのを考えてるのでガバガバです。
numpyとかのおかげでかなり簡単にできるみたいですね。
別のプログラムで使うことを考えてcsvに結果(ファイル名、花実数)を吐き出すようにしました
import numpy as np import matplotlib.pyplot as plt import cv2 import csv from numpy.random import * # 画像の幅 width = 130 # 最小の花実の直径 min_r = 5 # 最大の花実の直径 max_r = 13 # 最大の花実の個数 max_count = 20 # 最低の花実の個数 min_count = 0 # ベースとなる緑(RGB)(乱数により揺れるので、適当で大丈夫) green_base = (72, 107, 12) # ベースとなる茶色 brown_base = (120, 101, 86) # 花の色 flower_color = (237, 223, 29) # 作る画像の枚数 create_count = 1000 # データの保存先 data_path = "./data/dst/" # 画像の名前 image_name = "train_image" # 拡張子 extension = ".jpg" # 背景画像 back_image = np.full((height, width, 3), 0, dtype=np.uint8) # 背景色のベースカラー(緑、茶色) color_pattern = np.array([green_base, brown_base]) # 10飛びのarrayを作成 list = np.array(range(0,width + 1,10)) with open('train_images.csv', 'w') as f: writer = csv.writer(f) # headerの書き込み writer.writerow(["file_name", "count"]) for i in range(create_count): # 半径10の円で 埋めていく for x in list: for y in list: cv2.circle(back_image, (x, y), 10, (color_pattern[randint(0, 2)] + randint(0, 20,(1,3))[0]).tolist(), thickness=-1) # ガウシアンフィルターを書けて平滑化 blured_back_image = cv2.GaussianBlur(back_image, (7, 7), 0) # 花実の数を生成 flower_count = randint(min_count , max_count) for j in range(flower_count): #花実の大きさをランダムで生成 flower_radius = randint(min_r, max_r) position = (randint(0, width + 1), randint(0, width + 1)) cv2.circle(blured_back_image, position, flower_radius, flower_color, thickness= -1) # bgr になっているので rgb に変換する im_rgb = cv2.cvtColor(blured_back_image, cv2.COLOR_BGR2RGB) save_name = image_name + str(i) + extension cv2.imwrite(data_path + save_name, im_rgb) writer.writerow([save_name, flower_count])
色とかも適当にベースカラーきめてrandで適当なvarianceを付けるみたなことをしてて
ほんとにこれで大丈夫か?みたいなところはあります。
実行結果
画像達は一応できてそうです
csvもfile名とそれの花実の数を吐き出されていますね。
$ head -n 10 train_images.csv file_name,count train_image0.jpg,9 train_image1.jpg,16 train_image2.jpg,11 train_image3.jpg,11 train_image4.jpg,2 train_image5.jpg,11 train_image6.jpg,17 train_image7.jpg,15 train_image8.jpg,18
研究が環境系なのでAWSとかへの知見がなく、研究費がおりないとか言われたので、一旦Google Colab とかでtrainは走らせたいと思います
自分でマシン組めとか言われましたが、流石にしんどいので(時間あればやりたいですが...)
https://colab.research.google.com/
google drive とかに↑で作った画像とcsv置いて、API叩けばgoogle colab上にfetchしてこれるみたいなので、それでいいかなーと思ってます
RxSwift + UIRefreshControl + MVVMで幸せになる
iOSにはUIRefreshControlというものがあります(twitterでいうところのシュッテしてポンってなるやつ)
あれ結構使うわりにはいちいちpropertyを設定するのが面倒だったので
MVVMを使うこと前提にRxRefreshingControl
というものを作ってしまえば楽じゃねと思い作りました。
import RxSwift import RxCocoa protocol RefreshableModel: class { func refresh() var refreshingState: Driver<Bool> { get } } class RxRefreshControl: UIRefreshControl { private weak var refreshableModel: RefreshableModel? private let disposeBag = DisposeBag() private override init() { super.init() rx.controlEvent(.valueChanged) .asDriver() .drive(onNext: { [weak self] _ in self?.refreshableModel?.refresh() }).disposed(by: disposeBag) } required init?(coder aDecoder: NSCoder) { fatalError("init coder has not been implemented") } convenience init(_ refreshableModel: RefreshableModel) { self.init() self.refreshableModel = refreshableModel } func setupEndRefresingListener(_ listner: @escaping () -> Void) { refreshableModel?.refreshingState .drive(onNext: { [weak self] _ in self?.endRefreshing() listner() }).disposed(by: disposeBag) } }
refreshingControlの役割としてはシュってしたらrefreshのリクエストが走り、responseが会ったらポンってなってほしいだけです。
そのため、VMをRefreshableModelに準拠させ、①refreshの状態をDriverによって流してやること、②refresh時APIを叩くメソッド を用意しといて上げれば
ViewController では
private lazy var refreshControl: RxRefreshControl = { return RxRefreshControl(viewModel) }()
としてrefreshControlをしてrefreshしたいtableView
やcollectionView
に対して.addSubView(refreshControl)
して
もしrefresh時のコールバックを仕込みたければviewDidLoad
などで
refreshControl.setupEndRefreshingListener {
// 任意のコールバック
}
とすれば良くなるわけですね。
これでrefreshが少しは扱いやすくなるかと思います
植物工場のための画像認識
会社で働きながらB4で卒論を書いているという謎のステータスなのでたまにはメモ程度に卒論について
僕が専攻しているところが工学部でありながらなんでもありな学科(経済系からいわゆるAI系や環境系など)ですが
僕はキャンパスが家から近いのが絶対条件だったので人気がなさそうな環境系を選びました。
そしたら”植物工場を作るために植物の状態を観測したい、ディープラーニングってやつでなんとかして”って感じの研究室で
運良く趣味の延長線上みたいなことをやることになりました。
そのため、備忘程度に呼んだ論文や軽く調べたことを書こうと思います。
生育状態の観測
”植物の生育状態がどうか”というのを継続的に&&自動的に観測するというのが、植物工場実現の上で重要のようです。
例えば、生育状態を元にしたフィードバック制御で温度管理などを行うようで、
”生育状態”をいくつかにブレイクダウンし継続的に計測をしたいみたいですね。
”生育状態”の指標として、例えば、花実の数、葉面積などのメトリクスがあり、これらを計測したいというニーズがあるみたいです。
そんなわけで最初はこの論文を読みました。
①Deep Count: Fruit Counting Based on Deep Simulated Learning
この論文のすごいとこ
① trainの画像データは実際の写真でなく人工的に作ったもので代用可能である
② 花実の数を90%程度の精度で当てる
いくつかある学習済みのモデルは植物の種類が変わったとしても多少大丈夫程度のロバストさはあるものの、
やはり精度は足りないようで、植物の種類、生育環境ごとにモデルを組み直すのがよくあるみたいなようですが、
アノテーション済みのデータセットの不足(手間がかかる)により、あまり芳しくない結果となることが多いようです。
そのため、人工的に訓練データを作成してしまえばええやんというコンセプトで
実際にそこそこの精度を出しているのがこの論文のようです。
いくつか関連しそうな論文の中でまず手につけるタスクとして一番これがイメージに近いかなという感じです。
コードを作って共有できればと思います。
②Data synthesis methods for semantic segmentation in agriculture: A Capsicum annuum dataset. Computers and Electronics in Agriculture
この論文のすごいところ
① 花実にとどまらずに植物の3次元データからtrainDataを作り出せるっぽい?
まだ読み途中ですが、わりと有用そう。
ただ本筋とはそれそうなので時間があったら読む
やはりapplicationの人間なのでacademicな議論は向いてないなぁと思った一日でした
pecoがめっちゃ便利だった
ssh hogehoge
なんかを使ってインスタンスの中いって状況みたり
いくつかのprojectのファイルを行き来するために頻繁に cd ~/hoge
とか打つことが多くて
若干ストレスがあったのですが、どうやらpecoってものが便利らしく使ってみたという話です。
READMEを見るだけでワクワクしてきますね。
pecoとは
peco can be a great tool to filter stuff like logs, process stats, find files, because unlike grep, you can type as you think and look through the current results.
ということで、ls
とかps
で吐き出される出力をインクリメンタルにサーチ→選択ができ、
選択した値を使ってゴニョゴニョしたりできるみたいです。
使ってみた
Homebrewを使ってとりあえずインストール
$ brew install peco
こんだけです。
$ ls -al | peco
みたいにpecoに渡して上げると
エモいインターフェイスになり検索できるわけですね。良い。。。
さて一番最初にいったような いくつかのprojectのファイルを行き来するために頻繁に cd ~/hoge
とかの解消法ですが
bashを使っている方なら ~/.bashrc
とかに
function pcd() { local target=$(ls $HOME/(探したいdir) | grep -e '絞りたい語句1' -e '絞り込みたい語句2' | peco) if [ ! -z "$target" ]; then cd $HOME/(探したいdir)/$target fi }
みたいなものを作っておき source ~/.bashrc
とかして更新
$ pcd
として選択すれば指定のディレクトリに遷移できるわけです。きもちいですね
あと~/.ssh/config
とかに設定をまとめておけば
function pssh() { local host=$(grep -r 'Host ' $HOME/.ssh/* | cut -d' ' -f2 | sort | peco) if [ ! -z "$host" ]; then ssh "$host" fi }
で接続先を選んでsshとかができます。
iOSアプリでのstateごとのviewを出し分ける話
こんにちは
趣味みたいな仕事でstateごとにviewを出し分けたくなったのでその話です。
アプリって基本的に
遷移→APIのコール、loading→成功したらそれを表示、失敗したらエラー画面の表示
の繰り返しになり、
AndroidならViewAnimator | Android Developersを使って
同一階層にstateごとにviewを作成し、viewAnimatorのdisplayChildにbindして上げるとキレイに解決できますが
iOS appの場合それに当たるものなんだろうなーと思い奮闘していました。
正直ベストプラクティスな感じはしませんが一応メモ程度に残します。
やりたいこと
Loading | Error |
---|---|
みたいな画面を統一的に出したい
できたもの
とりあえず使い回すためにゴリゴリviewを書いていく
まずはloading側
class LoadingStateView: UIView { private lazy var indicator: UIActivityIndicatorView = { let indicator = UIActivityIndicatorView() indicator.color = UIColor.black return indicator }() override init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setup() } private func setup() { backgroundColor = UIColor.vcBg addSubview(indicator) indicator.startAnimating() indicator.snp.makeConstraints { $0.width.height.equalTo(50) $0.center.equalToSuperview() } } }
次にError側
import RxSwift import RxCocoa class ErrorStateView: UIView { private let disposeBag = DisposeBag() private lazy var reloadButton: UIButton = { let button = UIButton() button.setTitle("もう一回読み込む", for: .normal) button.titleLabel?.font = UIFont(name: "HiraKakuProN-W3", size: 15) button.backgroundColor = // pinkっぽい色 button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15) button.layer.cornerRadius = button.intrinsicContentSize.height / 2 return button }() private lazy var messageLabel: UILabel = { let label = UILabel() label.font = UIFont(name: "HiraKakuProN-W3", size: 15) label.textColor = UIColor.black label.text = "通信に失敗しました" label.sizeToFit() return label }() private lazy var stackView: UIStackView = { let stackView = UIStackView() stackView.addArrangedSubview(messageLabel) stackView.addArrangedSubview(reloadButton) stackView.spacing = 10.0 stackView.axis = .vertical stackView.distribution = .fill stackView.alignment = .center return stackView }() override init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setup() } private func setup() { backgroundColor = UIColor.vcBg addSubview(stackView) stackView.snp.makeConstraints { $0.right.left.equalToSuperview() $0.centerY.equalToSuperview() } } func setupButtonClickListener(handler: @escaping () -> Void) { reloadButton.rx.tap .asDriver(onErrorDriveWith: Driver.empty()) .drive(onNext: { handler() }).disposed(by: disposeBag) } }
snapKitを使ってゴリゴリconstraintsとか書くのが好きなのでこんな感じになりました。
これらを使いまわせばいけそうですね
使い方(storyBoardの設定)
普通はviewのしたにゴリゴリviewを配置していくのところをとりあえず3つのviewを作ってみる(successタイポしてた...)
各Viewの大きさはメインのview(SuccessView)に合わせて適当に配置
LoadingStateView, ErrorStateViewにはそれぞれさっきのcustomClassを当ててあげる
あとはIBから使いたいViewControllerにoutletをつなぎましょう
使い方(コード上の設定)
初心者ながらMVVMっぽく書きたくなったので
class ProductRankingsPageViewController: UIViewController { private let disposeBag = DisposeBag() @IBOutlet weak private var loadingView: LoadingStateView! @IBOutlet weak private var mainView: UIView! @IBOutlet weak private var errorView: ErrorStateView! private var stateViews: [UIView] { return [loadingView, mainView, errorView] } private let viewModel = TestPageViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.viewDidLoad() setupListener() } private func setupListener(){ errorView.setupButtonClickListener { [weak self] in self?.viewModel.reconnect() } viewModel.viewState .drive(onNext: { [weak self] state in guard let strongSelf = self else { return } strongSelf.view.bringSubview(toFront: (strongSelf.stateViews[state.rawValue])) strongSelf.collectionView.reloadData() strongSelf.collectionView.setContentOffset(CGPoint.zero, animated: false) }).disposed(by: disposeBag) } }
こんな感じになっており
さっきの3つのviewを配列として返すようなcomputed propertyを作っておき、
viewの状態をsubscribeして、現状のviewを最前列に持ってくるみたいなロジックです。(つねに3つのView持ってるの不健全っぽい)
また、errorの時のボタンを押した時の挙動をセットしてあげています。
viewModelはこんな感じで
import RxSwift import RxCocoa class TestPageViewModel { private let state: BehaviorRelay<LoadingState> = BehaviorRelay(value: .loading) private var busy = false var viewState: Driver<LoadingState> { return state.asDriver(onErrorDriveWith: Driver.empty()) } func viewDidLoad(){ state.accept(.loading) fetch() } func reconnect(){ state.accept(.loading) fetch() } private func fetch() { let single = // rxMoyaを使ってSingle<T>を返すようなrequestをここに書いています single.observeOn(MainScheduler.instance) .subscribe { [weak self] event in guard let strongSelf = self else { return } switch event { case .success(let dailyRanking, let categoryRanking): // 成功時の処理 strongSelf.state.accept(.success) case .error: // 失敗時の処理 strongSelf.state.accept(.failure) } strongSelf.busy = false strongSelf.refreshing.accept(false) }.disposed(by: disposeBag) } }
stateにerrorを流すことは基本的にないのでviewStateでdriverに変換し、適当にerrorは潰して上げます。
APIのコールが成功→state.accept(.sucess)
APIのコールが失敗→state.accept(.failure)
としてます。
ここでLoadingStateは簡単なEnumで
enum LoadingState: Int { case loading = 0 case success = 1 case failure = 2 }
こんなものです。
これで動くかと思います。
MVVMは状態やインスタンスの変数をVMに吐き出せるのでVCがスッキリしていいですね。
VCにすべて書こうとするならば
private var state: LoadingState = .loading { didSet { DispatchQueue.main.async { self.view.bringSubview(toFront: self.stateViews[self.state.rawValue]) } } }
みたいにしてあげればよいかなという感じです。
ブログはじめてみた話
こんにちはプログラミングとかで綴っておきたいことが増えたのでブログを初めてみました