OpenAI Gym の MountainCar が難しいという話

Vanilla Policy Gradient を勉強・実装している。CartPole はうまく学習できるのに MountainCar の学習が進まない reward -200 であり続けるという減少にハマった。RL のデバッグは本当に難しく、観察してもよくわからなかった。検索してみるとまさに同じ減少で困っている人が解決方法を紹介していてくれた。結論だけ書くと official env の reward が厳しすぎるので独自の reward を定義しようというものであった。納得。あとから見返してみれば明白なのだが気づけなかった。

Solving Curious case of MountainCar reward problem using OpenAI Gym, Keras, TensorFlow in Python – A Software Engineer's Journal

Colab + PyTorch Lightning + Comet ML + Bert

Colab + PyTorch Lightning + Comet ML - higepon blog の続き。

目標

Tensorflow 2.0 コンペで自分が書いた training のコードを Colab + PyTorch Lightning + CometML に移植する。移植したことでメリットがあるかを検証する。

Google Drive の利用とその失敗

Colab を利用すると 12時間毎にマシンがリセットされる。つまり巨大な kaggle の dataset や生成物が全て消えてしまう。生成物の例としては train/valid split したものとか modelを save したものとか。これらを毎回生成するのは無駄なので Google Drive 上に置いておく。これをダウンロードすれば良い。というコードを書いたのだがうまく行かなかった。生成にかかる時間はほほゼロになったが Google Drive から生成物をダウンロードする時間が支配的になってしまった。ボツ。

移植

transformers Bert のモデルを TF2.0 で使ったモデルにする。

import torch
from transformers import (
    BertModel,
    BertTokenizer
)

bert_model_name = 'bert-large-uncased-whole-word-masking-finetuned-squad'
tokenizer = BertTokenizer.from_pretrained(bert_model_name)
bert = BertModel.from_pretrained(bert_model_name)

次に LightningModule の Optional な callback をコメントアウトする。validation_step, validation_end, test_step, test_end は Optional である。 forward と training_step を埋めてこんな感じになった。batch が dataloader の返すものかな。

    def training_step(self, batch, batch_nb):
        # batch
        input_ids, attention_mask, token_type_ids, no_answers, y_batch = batch
         
        # fwd
        y_hat = self.forward(input_ids, attention_mask, token_type_ids)
        
        # loss
        loss = loss_fn(y_hat, y_batch, no_answers)
        
        # logs
        tensorboard_logs = {'train_loss': loss}
        experiment.log_metric('train_loss', loss.detach().cpu().numpy(), step=self.global_step, epoch=self.current_epoch, include_context=True)
        return {'loss': loss, 'log': tensorboard_logs}

謎の CUDA エラー

データローダーの実装が終わり end to end で動かしてみると以下のエラー。

RuntimeError: CUDA error: device-side assert triggered

付加情報がない。普通の PyTorch NN なら同じモデルを CPU で動かすと詳細なエラーが分かるのだが transformer Bert はどこかで GPU 前提っぽい。と思ったが CPU 上で動かせた。num_classes が 5 であるべきところが別の config file が使われて 2 になっていた。これが cross entropy loss を計算するところでこけてた。

Colab + PyTorch Lightning + Comet ML

背景

Kaggle の上位ランカーが PyTorch Lightning について言及していたの試してみる。同様に Comet ML も。Kaggle の試行錯誤を Colab (or Colab Pro) に移行できるかもあわせて検討する。

ToDO

以下淡々と ToDOをこなしていきメモを残す。

Lightning 基礎

  • Lightning の transformers example を Colab 単体で動かす。
  • 上記の dataloader を少ないデータに改造 end to end で素早く回せるようにする。

Lightning training resume

Checkpointing によると2つ方法がある。

1つめ

Trainer の resume_from_checkpoint 引数で checkpoint のパスを指定する。

resume = False
if resume:
  trainer = pl.Trainer(gpus=1,
                                    max_epochs=4,
                                    resume_from_checkpoint='lightning_logs/version_0/checkpoints/_ckpt_epoch_1.ckpt')  
else:
  !rm -rf lightning_logs/  
  trainer = pl.Trainer(gpus=1, max_epochs=2)

trainer.fit(bert_finetuner) 

まず最初の training 時には以下のようなログ。

Epoch 1: 100%|██████████| 4/4 [00:01<00:00,  2.22batch/s, avg_val_acc=0.2, batch_idx=2, gpu=0, loss=1.138, v_num=0, val_loss=1.12]
Epoch 2: 100%|██████████| 4/4 [00:29<00:00,  7.26s/batch, avg_val_acc=0.2, batch_idx=2, gpu=0, loss=1.084, v_num=0, val_loss=1.08]

次に resume すると

Epoch 2: 100%|██████████| 4/4 [00:01<00:00,  2.22batch/s, avg_val_acc=0.6, batch_idx=2, gpu=0, loss=0.992, v_num=1, val_loss=0.916]
Epoch 3: 100%|██████████| 4/4 [00:01<00:00,  2.34batch/s, avg_val_acc=0.6, batch_idx=2, gpu=0, loss=0.952, v_num=1, val_loss=0.844]
Epoch 4: 100%|██████████| 4/4 [00:05<00:00,  1.14s/batch, avg_val_acc=0.8, batch_idx=2, gpu=0, loss=0.911, v_num=1, val_loss=0.751]
Epoch 5: 100%|██████████| 4/4 [00:31<00:00,  7.86s/batch, avg_val_acc=1, batch_idx=2, gpu=0, loss=0.865, v_num=1, val_loss=0.659]

training をresumeできる。ただし Epoch 2 を2回やっているように見える。注意しないといけないのは dataloader は state が resume されるわけではないこと。なので shuffle 前提であるとどこかに書いてあった。

2つめ

experiment version 番号を使う方法。experiment version については、pytorch_lightning.loggers.tensorboard moduleの version 引数に説明がある。version は上記のversion_0 とか version_1 が自動で割り振られるをやめて特定の物を指定する。

コードは

from pytorch_lightning.logging.tensorboard import TensorBoardLogger
bert_finetuner = BertMNLIFinetuner()

resume = False # True

logger = TensorBoardLogger(save_dir='experiments', version=10)
if resume:
  trainer = pl.Trainer(gpus=1, max_epochs=5, logger=logger)
else:
  !rm -rf default/version_10/  
  trainer = pl.Trainer(gpus=1, max_epochs=2, logger=logger)
trainer.fit(bert_finetuner) 

Train 開始。

Epoch 1: 100%|██████████| 4/4 [00:02<00:00,  1.49batch/s, avg_val_acc=0, batch_idx=2, gpu=0, loss=1.122, v_num=10, val_loss=1.26]
Epoch 2: 100%|██████████| 4/4 [00:29<00:00,  7.35s/batch, avg_val_acc=0.2, batch_idx=2, gpu=0, loss=1.032, v_num=10, val_loss=1.18]

Train resume。

INFO:root:model and trainer restored from checkpoint: /content/default/version_10/checkpoints/_ckpt_epoch_1.ckpt
Epoch 2: 100%|██████████| 4/4 [00:01<00:00,  1.54batch/s, avg_val_acc=1, batch_idx=2, gpu=0, loss=0.844, v_num=10, val_loss=0.56]
Epoch 3:  75%|███████▌  | 3/4 [00:01<00:00,  1.54batch/s, avg_val_acc=1, batch_idx=2, gpu=0, loss=0.767, v_num=10, val_loss=0.56]
Epoch 3: 100%|██████████| 4/4 [00:02<00:00,  1.54batch/s, avg_val_acc=1, batch_idx=2, gpu=0, loss=0.767, v_num=10, val_loss=0.446]
Epoch 4: 100%|██████████| 4/4 [00:05<00:00,  1.54batch/s, avg_val_acc=1, batch_idx=2, gpu=0, loss=0.696, v_num=10, val_loss=0.356]
Epoch 5: 100%|██████████| 4/4 [00:31<00:00,  7.81s/batch, avg_val_acc=1, batch_idx=2, gpu=0, loss=0.619, v_num=10, val_loss=0.287]

こちらの方法が自動的に新しい version が振られることなく自然で使いやすいと思う。

Google Drive への保存

Colab はいつセッションが切れるか分からないので Google Drive に保存する。以下のようなコードで Google Drive がマウントされていない場合のみにマウント処理。

from google.colab import drive
from pathlib import Path

def mount_drive_if_necessary():
    drive_path = Path('/content/drive')
    if not drive_path.exists():
      drive.mount(str(drive_path))

mount_drive_if_necessary()

train のコードは

from pytorch_lightning.logging.tensorboard import TensorBoardLogger
bert_finetuner = BertMNLIFinetuner()

resume = True

save_root_path = Path('/content/drive/My Drive/kaggle/tf2.0')
logger = TensorBoardLogger(save_dir= save_root_path / 'logs', version=10, name='simple_bert')
if resume:
  trainer = pl.Trainer(gpus=1, max_epochs=5, logger=logger, default_save_path=save_root_path)
else:
  !rm -rf default/version_10/  
  trainer = pl.Trainer(gpus=1, max_epochs=2, logger=logger, default_save_path=save_root_path)

trainer.fit(bert_finetuner) 

実際の check points と log は以下のように保存される。

/content/drive/My Drive/kaggle/tf2.0
/content/drive/My Drive/kaggle/tf2.0/logs
/content/drive/My Drive/kaggle/tf2.0/logs/simple_bert
/content/drive/My Drive/kaggle/tf2.0/logs/simple_bert/version_10
/content/drive/My Drive/kaggle/tf2.0/logs/simple_bert/version_10/events.out.tfevents.1582691888.8883d33e5953.1505.0
/content/drive/My Drive/kaggle/tf2.0/logs/simple_bert/version_10/meta_tags.csv
/content/drive/My Drive/kaggle/tf2.0/logs/simple_bert/version_10/events.out.tfevents.1582692040.8883d33e5953.1505.1
/content/drive/My Drive/kaggle/tf2.0/simple_bert
/content/drive/My Drive/kaggle/tf2.0/simple_bert/version_10
/content/drive/My Drive/kaggle/tf2.0/simple_bert/version_10/checkpoints
/content/drive/My Drive/kaggle/tf2.0/simple_bert/version_10/checkpoints/_ckpt_epoch_1.ckpt
/content/drive/My Drive/kaggle/tf2.0/simple_bert/version_10/checkpoints/_ckpt_epoch_4.ckpt

ハイパーパラメータを checkpoint と同時に保存する

以下のような警告が出ているので調べる。

UserWarning: Did not find hyperparameters at model.hparams. Saving checkpoint without hyperparameters
  "Did not find hyperparameters at model.hparams. Saving checkpoint without"

hparams が checkpoint と一緒に保存されてうれしいのは load_from_checkpoint で load するとき。Trainer 経由で resume するときはその限りではない気がする。 ちなみに hparams はモデルで

class BertMNLIFinetuner(pl.LightningModule):

    def __init__(self, hparams):
        super(BertMNLIFinetuner, self).__init__()
        
        self.bert = bert
        self.W = nn.Linear(bert.config.hidden_size, 3)
        self.num_classes = 3
        self.hparams = hparams
        self.learning_rate = hparams.learning_rate

...snip...
hparams = Namespace(**{'learning_rate': 2e-05})

bert_finetuner = BertMNLIFinetuner(hparams)

こうすることで hparams が別ファイルとして保存される。

! cat tf2.0/logs/simple_bert/version_10/meta_tags.csv
key,value
learning_rate,2e-05

この hparams と comet.ml などの連携も調べなければいけない。

Comet ML

以下のように Trainer に CometLogger を渡せばOK。オフラインモードはインターネット接続がない環境向けなので使わない。

comet_logger = CometLogger(
    api_key='',
    workspace='higepon',
    project_name="simple-bert-test", # Optional
    experiment_name="my_experiment_name_long7",
    rest_api_key = '')

trainer = pl.Trainer(gpus=1, max_epochs=30, logger=comet_logger, default_save_path=save_root_path)

Code タブ

各 Experiment ごとにソースコードを紐付けることができる。そうすれば各バージョンごとに Diff がとれるようになる。Colab の場合以下のようにソースコードを Comet に送る。

code = ''
for cell_in in In:
  code += cell_in + '\n'

comet_logger.experiment.set_code(code)

f:id:higepon:20200227154801p:plain

Metrics を追加する

デフォルトでは train loss しか Comet で見ることができない。自分の場合は val loss も欲しかったので以下のようにした。

    def validation_end(self, outputs):
        avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean()
        avg_val_acc = torch.stack([x['val_acc'] for x in outputs]).mean()

        tensorboard_logs = {'val_loss': avg_loss, 'avg_val_acc': avg_val_acc}
        self.logger.experiment.log_metric('val_loss', avg_loss.detach().cpu().numpy(), step=self.global_step, epoch=self.current_epoch, include_context=True)
        return {'avg_val_loss': avg_loss, 'progress_bar': tensorboard_logs}

training の resume

CometLogger は training の resume に対応していないことがわかった(pytorch-lightning/comet.py)。というか TensorboardLogger が resume に対応しているのだね。つまり Lightning と Comet を同時に使いたいなら CometLogger 経由ではなく Comet API を直接叩くのが良さそう。

存在する experiment に追記できてる?

Experiment Overview - Comet.ml よると experiment.get_key(self) を利用して ExistingExperiment を作れる。実際に試してみたらうまく行った。

if resume:
   experiment = comet_ml.ExistingExperiment(api_key='',
                                            previous_experiment=experiment_key,
                                      project_name="simple-bert-test",
                                      workspace='higepon',
                                      ) 
else:
  experiment = comet_ml.Experiment(api_key='',
                                      project_name="simple-bert-test",
                                      workspace='higepon',
                                      )

apex amp を有効にする

Trainer に use_amp=True を渡す。Colab で環境を整えるのはちょっと面倒。

try:
  from apex import amp
except ImportError:
  !pip install https://download.pytorch.org/whl/cu100/torch-1.2.0-cp36-cp36m-manylinux1_x86_64.whl
  !pip install https://download.pytorch.org/whl/cu100/torchvision-0.4.0-cp36-cp36m-manylinux1_x86_64.whl    
  !git clone https://github.com/NVIDIA/apex
  !cd apex;pip3 install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" .

! pip -q install pytorch-lightning==0.6.0
! pip -q install transformers==2.1.1
! pip -q install comet_ml

続きます。

GCP + Visual Studio Code 開発環境構築

前提

GCP への ssh 接続。GCP の AI notebooks インスタンスに ssh できなかったので調べた - higepon blog

目標

  • Visual Studio Code のリモート接続を利用して GCP 上のコードを快適に編集。
  • git 周りの操作もそのまま行いたい。
  • Visual Studio Code 自体の習熟。
  • Kaggle 用に directory layout も最適化。

手順

GCP の AI notebooks インスタンスに ssh できなかったので調べた

動機

Visual Studio Code でコードをリモート編集したい。そのためには local Mac からインスタンスssh が必要。

事実

ssh -v

ssh -v  [外部IPアドレス]
OpenSSH_7.9p1, LibreSSL 2.7.3
debug1: Reading configuration data /Users/higepon/.ssh/config
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 48: Applying options for *
debug1: Connecting to [外部IPアドレス] [[外部IPアドレス]] port 22.
debug1: connect to address[外部IPアドレス] port 22: Operation timed out
ssh: connect to host [外部IPアドレス] port 22: Operation timed out

ファイアーウォールの設定

まずヘルプドキュメントを読む。

設定してみよう。"VPC ネットワーク" - "ファイアーウォールルール" を開く。

  1. ファイアウォール ルールの作成
  2. 名前: higepon-kaggle-ssh
  3. ネットワーク:インスタンスのネットワークと同じものを選ぶこと!
  4. 優先度:999
  5. 上り
  6. ターゲットタグ:higepon-kaggle-ssh
  7. ソースIPの範囲:0.0.0.0/0
  8. tcp:22
  9. インスタンス詳細設定
  10. ネットワークタグ:higepon-kaggle-ssh
  11. ssh -v [外部IPアドレス] で無事接続できた!

## 自分用メモ
インスタンス側で ssh 認証鍵を higepon と jupyter 両方で登録しておくこと。

Host kaggle-gcp
    HostName 外部IPアドレス
    User jupyter
    IdentityFile /Users/higepon/.ssh/google_compute_engine
    UserKnownHostsFile=/Users/higepon/.ssh/google_compute_known_hosts
    HostKeyAlias=compute.xxxxx
    IdentitiesOnly=yes
    CheckHostIP=no

突発性脳脊髄液減少症になり1ヶ月以上寝たきりだった話

時間のない人向けのまとめ

  • ある日突然激しい頭痛に見舞われ即入院。
  • 突発性脳脊髄液減少症低髄液圧症候群)と診断され手術。何らかの原因で、脳脊髄液が硬膜から漏れて髄液圧が低下することで頭痛などの症状が起こる病気。
  • 起立性頭痛(頭痛があるがしばらく横になると楽になる)がある場合は病院に行き症状を医師に伝えよう。
  • 確定診断・治療できる病院&先生はとても少ない。関東エリアならS病院T先生。

このまとめについて

ある日の朝、原因不明の激しい頭痛に見舞われた際の、入院、手術、回復までのまとめ。最近少しずつ知られるようになった病気で(ラジエーションハウス~放射線科の診断レポート~でも取り上げられた)。正しく診断してもらえない場合が多いようだ。病院をたらい回しにされ、心療内科うつ病と誤診されることもある。何ヶ月も原因不明で苦しむこともある。ここに詳細な記録を残すことで同じ病気にかかった人が、正しい診断・治療を受ける助けになれば幸いである。

感謝

闘病初期は原因不明で、一生寝たきりになる覚悟をした。はげまし支えてくれた妻と息子に深く感謝。また治療に尽力してくれた医療関係者のみなさんにも感謝と尊敬の念しかない。ある日突然出社できなくなって、職場の皆さんにも迷惑かけたが「仕事のことは気にせず治療に専念して」とバックアップしていただいて本当にありがたかった。

発症1日目

目が冷めて激しい頭痛に気づく。吐き気がひどい。トイレにこもり嘔吐。辛くて立てない。床にうずくまると少し楽になる気がする。頭痛持ちではないので、何らかの突発的なやばい病気かもしれない。近所の大きいK病院に行こうと思って着替えたが、まったく頭が回らない。僕の様子がおかしいことに気づいて妻がすぐにタクシーで病院に連れて行ってくれた。目がまわり頭痛がひどい。タクシーでは前かがみになりずっと下を向いて「早く着け」と念じていたのを覚えている。

病院到着後、すぐにストレッチャーで脳外科に運ばれ優先的に見てもらえた。この時点でベッドで横になると頭痛がすっと消えることに気づいていた。CT と MRI 検査。緊急を要する病気(脳梗塞くも膜下出血?)の兆候は見られないと言われ安心。寝ると頭痛が消えることを先生に伝えると「過去に一度だけ見たことがあるが、これは髄液が漏れているかも」と言われる(この先生の判断が得られたことが本当にラッキーだった)。CT と MRI を見ると若干脳が下垂しているように見えるとも。ただし髄液が本当に漏れているかはわからなかった。その場でK病院での入院が決まる。治療方針は水分を取りつつベッドで寝たきりの絶対安静。あとで調べてわかったのだが脳脊髄液減少症の治療方法は大きく分けて2つあり。2-3週間の保存的治療(ベッドで安静)とブラッドパッチ(硬膜外自家血注入)があり、後者は対応している病院がとても少ないのだ。

発症2日目から14日目 K病院

水分をたくさん取る必要があり毎日点滴。食事とトイレ以外はベッドで安静。起き上がるときは頭を低くして60度くらいのお辞儀をしている状態だと少し楽な気がする。食事はおにぎりなど、文字通り寝ながら食べた。

  • 発症2日目: 朝症状改善せず。水を飲んだが全て吐いた。昼から夕方まで気力がなくスマホで動画見ることさえもできない。寝ていても頭が痛い気がする。家族が見舞いに来て少し安心した。朝昼ご飯ほどんど食べられず。夕飯はほぼ食べた。夜も動画見る気は起きなかった。
  • 発症3日目: 朝起きたら少し楽になってた。顔を上げてもすぐには頭痛が来ない。昨夜までは後頭部にずーんと頭痛がきていたが、少し来るくらい。長く立ってると眉間が痛い気がする。本当にほぼ寝ているので生活に必要なものすべてを、ベッドから手が届く範囲においた。
  • 発症4日目: 可もなく不可もなく。悪くもなっていないか良くもなってない。
  • 発症5日目: 変わらず。昼にもう一度CT。寝ながらパン食べると楽なことを発見。ブラッドパッチについて調べ始める。病棟内の自販機まで前かがみで行ってみた。行けたけど。気持ち悪くなった。
  • 発症6日目: ようやく発見したが朝気持ち悪いことが多い。水分不足?。首のMRI撮影。車椅子で移動で気持ち悪くなりしばらくベッドで休んだ。夕方の時点でも気持ち悪い。
  • 発症7日目: 起き上がってざわざわと頭痛がが来るまでの時間が長くなった気がする。少し良くなってる。ただ顔の左側に若干に違和感。TODOリストを作ろうという気力が湧いたので多分良くなってる。首のMRIでほぼ低脳髄液症確定。自販機まで歩いてみた。往路は快調。復路で少しずつ頭が重くなる感じ。昨日よりはかなり良い。
  • 発症8日目: 今日からカフェインを薬として服用。頭痛に効くらしい。起き上がってどれくらいで頭痛が来るか測定しよう。47秒、50秒。カフェインのため夜寝られず。眠剤もらった。
  • 発症9日目: 時間測定。1:50まで待っても。頭痛来ず。頭が若干重くなる。寝過ぎの肩こりとかと同化してよく分からない。3分で来ることがわかった。デイルーム往復くらい。夜測定したら6分。
  • 発症10日目: デイルーム往復くらいで頭痛。昨日と変わらずか?10分歩いた。7分ぐらいで軽い頭痛が始まる。10分の時点で頭が痛くなり終了。痛さは二日酔いよりは軽い。寝たら徐々に良くなる。回復に3-4分かかる?シャワー許可され20分浴びたら頭が痛くなった。
  • 発症11日目: 10分座るとズキズキして寝たくなる。昨日かなり調べたおかげで自分の状態について先生にまともな質問ができるようになった。
    • 漏れている箇所は見つかったか?
      • 見つかってない。脳の下がり具合(下垂)の程度は低く症状は軽いと考えている。首のMRIで漏れているっぽいことはわかっている。もう一度MRIを取れば客観的に良くなっているか分かるだろう。
    • 専門医S病院のT先生に紹介状を書いて欲しい
      • 問題ない(ただし予約が2ヶ月先までいっぱい)。造影剤を入れてのRIで漏れている箇所の特定する検査は、K病院ではやってない。
  • 発症12日目: 午前に血液検査と首のMRI。詳細を職場の上司に送った。座って5分経っても平気だった。怖いので途中でやめた。K病院とS病院の、病院連携で2日後にS病院での初診外来予約が取れた(ここもラッキーである。予約がいっぱいのところにねじ込んでもらえた)。僕は行かずに妻がMRIなどの情報を持って届けることに。
  • 発症13日目: 朝試してみたら約7分で頭痛がくる。もう一度測定。ベッドで座るバージョン。10分で頭が重くなってきた。右目の奥が痛い感じ?頭振ると痛い。13分で「うー」やめよう。となった。
  • 発症14日目: 今日はS病院で妻が話を聞いてくれた。T先生からまず間違いなく突発性低髄液症候群であろうとの診断が。5日後にS病院に入院することが決定。ブラッドパッチの予定。

発症15日目から18日目 一時退院

  • 発症15日目: 一時退院して自宅へ。自宅でも寝たきり。夜は家ですき焼き。家族との時間はかけがいのないものだ。帰宅で調子にのったせいかめまいなどが酷い。夜にとても暴力的な夢を見て怖くなる。頭痛の影響だろうか。自分の人格が変わってしまったのかと思いとても凹んだ。
  • 発症16日目: 朝から気持ち悪くほぼ全てを吐いた。その後11:30am まだずっとゆっくりしてたらよくなった。目をつぶったらめまいがする。夕方には完全に良くなった。なんだったんだろう。
  • 発症18日目: とても気分がよい。ベッドにいる分には健康に思える。明日はS病院への入院。多少吐き気があったが、息子との最後のお風呂になるかもしれないので一緒に入った。頭痛が来るまでに上がらないといけないので5分と短いが、幸せである。

発症19日目から22日目

  • 発症19日目: タクシーでS病院へ。起き上がっている時間が最大限短くなるように妻ががんばってタクシーや入院手続きを最速でやってくれた。感謝。すぐにT先生登場。柔らかな雰囲気の患者に安心感を与える良い先生。K病院の情報からほぼ間違いなく低髄液だろうとのこと。早速 RI シンチ検査。放射性同位体の造影剤を腰椎から注入して、その造影剤の拡散具合で髄液の漏出やその箇所の特定をするもの。ネット情報だが、この検査がやれる病院がそもそも少ないようだ。
    • 腰に注射して髄液の抜き取り。麻酔の注射が少し痛い程度でほぼ痛くなかった。髄液抜き取りの際に、出てくる単位時間あたりの髄液が少ないことから髄圧が低いことが分かり、低髄液であることがほぼ確定と先生が仰った。その後造影剤を注入。これも麻酔のおかげか痛くない。処置後先生が髄液を見せてくれる。透明な液体である。こんなこともなければ見られない貴重な体験。先生は処置中に、雑談してくれてリラックスさせようとしてくれてるのが良くわかる。良い先生だ。病室に戻りうつ伏せで少し寝かされる。しばらくすると仰向けになるように言われる。腰は痛いと言うより全体的に重い感じ。何となくお腹が痛いなあというのが背中側にあるのに近い。この後は小便を我慢しつつ撮影を待つ。これを書いている時点であと20分。撮影のため起き上がる。うげげ。腰痛い。我慢できる範囲だがすごい腰痛めた感じ。寝てる時は気づかなかった。撮影はCTミエロとRI脳槽シンチ。寝てるだけ。予告通り頭も痛くなってきた。髄液抜かれた分痛くなるらしい。全ての移動は車椅子。
    • T先生の回診。RI で二箇所の漏れが見られる。首と背中二箇所のブラッドパッチの方向で同意書をもらった。CTでも似たような所見。念のため頭部撮影したが脳出血はなさそう。採取した髄液を国の研究に使って良いか?の書類ももらった。快諾した。今日は3回、明日の朝1回RIがある。2回目終了した時点で、漏れが確認されているわけだがあと2回やる必要あるなだろうか。
  • 発症20日目: 腰の痛みがかなり良くなり、座ってもずーんと痛くならない変な姿勢にならない。でもまだ痛い。立って歩けるようになった。髄液抜き取りの影響も減ったようだ。
    • RI 9時から。朝食は7:30くらい。昨夜は眠剤節約のため普通に就寝。23時くらいまで眠れなかった。
    • シャワーを10分ほど浴びたら眉間と後頭部が痛くなった。まだまだだね。
    • 明日はブラッドパッチなので夜に緊張。治る。絶対治ると自分に言い聞かせて眠りにつく。
  • 発症21日目:
    • ブラッドパッチを 10:30-10:50 に受けた。
      • T先生と看護師さん。そのまま病室で行った。うつ伏せで背中が湾曲するようにお腹にクッション。胸椎と腰椎に麻酔。麻酔は痛いが点滴の針を刺すのと同じ程度。まずは胸椎からブラッドパッチ。静脈からとった自分の血液を注入。背骨と肩甲骨が交わるところあたりに針を刺し注入。点滴針から自分の血を取って注入するらしいがもちろん見えない。針を刺したとき若干痛い。注入時は痛くないかなど様子を聞いてくれる。胸椎終了後に足の指をこっそり動かして何も異常はないことを確認。腰椎はRIをやったあたりから注入。先生の指示で看護師さんは僕の目をずっと観察していた。
      • ブラッドパッチ終了直後。起き上がって座るように言われる。頭が痛くならないか?と問われた。確かに痛くならない。血が固まるまで待つイメージだったのだが即効性があるとは知らなかった。成功して本当に良かった。いろいろな人の助けでこんなに早く治療してもらえたことは本当に幸運だった。
      • T先生術後のお言葉。あまり細かい症状は気にしないこと。もう治りましたよ。
    • 術後2時間経ったので食事とトイレ。立ったり歩いたり計25分。一切頭痛なし。頭痛の兆候すらなし。これは治ったね。泣ける。人生取り戻した!
    • 夕方傷口が痛むのでゆっくり。痛み止めをもらうほどではない。
    • 術後 10-14日はベッドで安静に、その後復職プランを相談しましょうと言われた。
  • 発症22日目:
    • 術部二ヶ所の痛みはかなり引いた。ゼロではないがすっと立ち上がれる。
    • 27分頭痛来ず。朝食や髭剃り、身支度できた。
    • 看護師さんの話
      • マッサージが原因でこの病気にになる人が多い。これを知ってから看護師さんはマッサージに行かなくなったらしい。
      • 発症から診断、治療まで1ヶ月以内というのはかなり珍しい。診断が早めにできたのがかなりラッキー。病気になったのはアンラッキーだがすごいラッキーなことらしい。運が悪いと心療内科にまわされ、心因性のものと診断されてしまうらしい。

発症23日目から35日目 自宅静養

術後2週間は自宅でベッドで静養。ここで油断しないようにひたすら寝る。

  • 発症23日目: 今日は点滴二本で午後に退院。T先生いわく。細かい小さな頭痛は気なさなくて良いとのこと。立ち上がると立ちくらみ。でも帰りのタクシーも全然平気だった。 発症24日目: ますます良くなった。まだ腰の痛みがあるかな。あと太った気がする。頭の痛みはほとんどない。
  • 発症25日目: 立ち上がったときのふらつきが減った気がする。
  • 発症26日目: 首の寝違えみたいな痛みもかなり減った。
  • 発症27日目: 首の寝違いみたいな痛みや、疲れ感も実は病気の症状でそれらが劇的に良くなってきた気がする。寝ていても疲れたりしない。まだ処置跡は重くて鈍い痛みがある。
  • 発症28日目: 処置跡以外は問題なし。と思ったが昼寝して目が覚めたらうっすら気持ち悪いような。気のせいのような。
  • 発症29日目: 朝目が覚めたらスッキリ。傷跡はまたうっすらと痛い。
  • 発症30日目: 頭は一切痛くない。ずっと寝ているので首肩の負担が高い。
  • 発症31日目: 頭痛くない。腰の傷口も痛くないかも。
  • 発症32日目: 腰と肩の痛みはかなり引いた。
  • 発症33日目: トイレに行くときもすっと立てる。1時間起きていても頭痛が来ない。
  • 発症34日目: 3時間寝ずに、起き上がってみる。快調である。ひょっとして治った?
  • 発症35日目: S病院でMRI・診察。先生から「治ったね!」とのお言葉。心配された合併症(脳の血腫)も見られず安心。1-3ヶ月かけて体力を戻していくようにとのこと。職場復帰は数日様子を見て体力が戻ってから。やったー。万歳!
    • ようやく起きて良い許可が出たので、職場に経過を連絡後。このブログをまとめた。

その後

  • 発症36日目: 昨夜首が痛くて目が覚める。朝には治ってた。姿勢の問題?
    • 近所のコンビニに歩いてみる。久しぶりの徒歩。体が重い。強歩大会の後半で足が棒になったときに似てる。ゆっくりなら歩ける。椅子に座ると背もたれのありがたみが分かる。
    • 結局起きてから寝るまで横になることはなく。起立性頭痛はなくなった。
    • 体力の衰えは否めない。
  • 発症37日目: 少しずつ日常へ。週明けに職場復帰をめざす。まずは近所の散歩から。

情報源

入院中はなるべく一次情報にあたるように病気について調べた。英語版 Wikipedia からリンクされている論文を読むのがおすすめ。