結論
次のコードを実行するだけです.
from google.colab import runtime
runtime.unassign()
背景/動機
Google Colaboratory は手軽に Deep Learning 用のクラウド計算環境を手に入れられる素晴らしいサービスですが,有料プランである Pro / Pro+ について,月額定額制からマイルド従量課金制に変更されました.
具体的に言うと,各プランに契約している場合に,月々,有効期限が90日間の「コンピューティングユニット」というものが付与されるようになりました.
ユニット数と月額料金はそれぞれ,
- Pro: 100(月額 1,072 円 + 税)
- Pro+: 500(月額 5,243 円 + 税)
- (無料版: 不明)
となっており,手持ちのコンピューティングユニットを使い切った場合は,ユニット数:100 を 1,072 円 + 税,ユニット数:500 を 5,243 円 + 税 で追加購入する必要がある仕様になりました.
筆者は Colab Pro ユーザーなので,(おそらく以前は)使いたい放題だった仕様から上記仕様に変更されたときは衝撃でした.
コンピューティングユニット数は,用いる計算環境によっても異なり,それぞれ以下のようになっています.
上表は一昔前の区分で,知らぬ間に以下の仕様のように,アクセラレータが明示的に指定できるようになりました(ただし,使用状況によっては,特に pro+ 以外で高スペックの A100 を指定すると,指定スペックと異なったものが割り当てられる場合あり).
[選択式] ハードウェア アクセラレータ | [選択式] GPUのタイプ | [選択式] ランタイムの仕様 | 1時間あたりの 使用ユニット数 | CPU | アクセラレータ |
None | – | 標準 | 約 0.08 | Intel(R) Xeon(R) CPU @ 2.20GHz ✕ 2 | – |
None | – | ハイメモリ | 約 0.12 | Intel(R) Xeon(R) CPU @ 2.20GHz ✕ 4 | – |
GPU | T4 | 標準 | 約 1.96 | Intel(R) Xeon(R) CPU @ 2.30GHz ✕ 2 | NVIDIA T4 |
GPU | T4 | ハイメモリ | 約 2.05 | Intel(R) Xeon(R) CPU @ 2.30GHz ✕ 4 | NVIDIA T4 |
GPU | V100 | 標準 | 約 5.36 | Intel(R) Xeon(R) CPU @ 2.30GHz ✕ 2 | NVIDIA V100 |
GPU | V100 | ハイメモリ | 約 5.45 | Intel(R) Xeon(R) CPU @ 2.00GHz ✕ 4 | NVIDIA V100 |
GPU | A100 | ハイメモリ | ※ | ※ | NVIDIA A100 |
TPU | – | 標準 | 約 1.96 | Intel(R) Xeon(R) CPU @ 2.30GHz ✕ 2 | TPU v2 |
TPU | – | ハイメモリ | 約 2.05 | Intel(R) Xeon(R) CPU @ 2.30GHz ✕ 40 | TPU v2 |
そして,気をつけなければならないのが,このユニット数は,計算時間ではなく,「ランタイムを接続している時間」によって減っていきます.
なんと,GPU / TPU のランタイムで計算走らせっぱなしで放っておくと,ユニット数がすぐに底をついてしまいます.
Pro+ ユーザーならばバックグラウンドで実行できてしまうため,全く気づかないうちにユニット数を浪費してしまいます.
このため,計算が終わったらこまめにランタイムを終了させたいところですが,計算時間の長い Deep Learning が終わるのを横目に待っているのも面倒です.
従いまして,タイトルの通り,計算が終わったりエラーで止まった場合に,「接続中のランタイムをコードから終了させる」ということについて調査した結果,関数が用意されていたのですごく簡単に実装できましたので紹介します.
コード実行例と説明
以下リンクの google/colab/runtime.py を見たら unassign という関数が用意されており,これをインポートし実行したら,コード実行でランタイムが終了できました.
以下のコードは,colab 上でコードブロック1,2,3のように実行していくような形で記載しました.
import time
t_start = time.perf_counter()
def t_elapsed(t):
return time.perf_counter() - t
print(t_elapsed(t=t_start))
# 0.00017659900001376627 : 経過時間を出力
from google.colab import runtime
runtime.unassign()
print(t_elapsed(t=t_start))
# ---------------------------------------------------------------------------
# NameError Traceback (most recent call last)
# <ipython-input-1-9bde2da830c3> in <module>
# ----> 1 print(t_elapsed(t=t_start))
#
# NameError: name 't_elapsed' is not defined
ブロック1では,経過時間を返す関数:t_elapsed を定義しています.
ブロック2で,ランタイム接続を切っています.
するとブロック3では,t_elapsed を呼び出しても「’t_elapsed’ は定義されていない」とエラーを返しています.
つまり,ブロック2できちんとランタイム接続が切れていることが分かります.
このブロック2のコードを計算の最後や,エラーの例外処理に仕込んでおけば,不用意にコンピューテkィングユニット数を浪費することの低減につながることでしょう(Slack や Discord への通知は,これの前に書くように注意が必要です).
コメント