Post Bank

よわいエンジニアのブログ

Whitenoiseの仕様変更

v4.0より前

DjangoプロジェクトををHerokuでデプロイする時に、Django girls tutorialに従ってやると、wsgi.pyファイルに以下のように書くと思う。

wsgi.py

import os

from django.core.wsgi import get_wsgi_application
from whitenoise.django import DjangoWhiteNoise

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')

application = get_wsgi_application()
application = DjangoWhiteNoise(application)

v4.0以降

これで今までは動作してたのだが、ある時急にローカル環境がおかしくなってパッケージを一通り入れ直したら、whitenoiseのバージョンが上がってて、仕様変更が入っていた。

内容はこちらwsgi.pyファイルに上のように書く必要がなくなって、settings.pyのmiddlewareに追記すれば良くなったらしい。まずwsgi.pyファイルはプロジェクト生成した時のままで良くなって、こんな感じ。

wsgi.py

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')

application = get_wsgi_application()

settings.pyはMIDDLEWAREwhitenoise.middleware.WhiteNoiseMiddlewareを加えるのと、STATICFILES_STORAGEwhitenoise.django.GzipManifestStaticFilesStorageからwhitenoise.storage.CompressedManifestStaticFilesStorageに変更する。

settings.py

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ここを見ると、whitenoiseSecurityMiddlewareのすぐ後ろに書いて、他のmiddlewareよりも前に書かなければいけないらしい。

いつからこうなった

 Voicyで堤修一さんのエンジニアと人生というタイトルのチャンネルを聞いてみた。ゲストは個人アプリ開発者のうめのん氏。うめのん氏のブログはこちら。堤さんのブログはこちら

voicy.jp

 お二人とも特定の会社には属さずに(堤さんはアメリカの会社に一応籍は置いているみたいだが)、フリーで活動されているアプリ開発エンジニアだ。会社に属して平凡な毎日を送っている私からすると、羨ましいような生き方をされているという印象を受けた。羨ましいというのは、会社に縛られずに気楽に生きていけていいな〜とかいうことではなくて、アプリ開発という仕事を心から楽しんでいると聞いてて思ったからだ。堤さんなんかは、カンファレンスとかで海外に行っても、仕事の優先順位がかなり高くて、自由時間があったとしても観光に行きたいとも思わないそうだ。自分の寿命があと3年しかないと仮定した場合に、人のために今自分にできることは何かと考えたときに、それはやっぱりエンジニアとしてソフトウェアで世の中を少しでも変えていくことでしょ、という思考からそうなるらしい。人格者すぎる。そして実際に素晴らしいOSSも公開してるからすごすぎる。尊敬しかないです。

 他に印象に残ったのは、うめのん氏が1日で3時間しか仕事をしないということ。短時間で時間を区切ってやることで集中力が増して、作業効率が良くなるそうだ。そういえば茂木先生もフロー状態に入るには時間を区切ってやるのがいいってTwitterで言っていたような気がする。うめのん氏のこの方法は村上春樹のマネだそうで、村上春樹も朝6時間だけ執筆して昼は走って、みたいなルーティンらしくて、それを自分でも試しにやってみたそう。ただうめのん氏は6時間だと長すぎてだれてしまう時間できてしまったので、3時間にしたらめちゃくちゃ集中できたから3時間にしたと言ってた。うめのん氏のこの人のいいところを取り入れて、自分流にアレンジするという能力。そしてそれを継続する力。私も見習いたい。やっぱり出来る人というのは、自分流というのを持っている。

 このチャンネル、エンジニアが聞いたら面白いと思う。他人の仕事の仕方とか考え方とか生き方を聞くのは面白い。自分のスキルを磨いてきて腕一本で食っていっているような凄腕エンジニアの話だから少し自分の卑小さが惨めになって若干凹んだりもするけど、それもある種の刺激になって良い。エンジニアの皆さんは聞いてみては?

うめのん氏のアプリはこちら。

TensorFlow LiteのPost-training quantization

TensorFlow Lite公式ページに従って、以下のコードでweightの量子化をしてみた。

import tensorflow as tf

converter = tf.lite.TocoConverter.from_saved_model(saved_model_dir)
converter.post_training_quantize = True
tflite_quantized_model = converter.convert()
open("quantized_model.tflite", "wb").write(tflite_quantized_model)

これを実行すると、weightはfloat32からuint8に変換されるが、計算自体はfloat32のまま浮動小数点数で行われるそうだ。だから、Interpreter#runの入力はfloat32のまま。Post-training quantizationをしたからといって、デモでQuantized modelへの入力をuint8にしているようには出来ない。Full quantization modelを作成するには、Quantization-aware trainingをしないといけないらしい。参考

Androidで非同期処理【Android、Java】

AsyncTaskを使って非同期処理

適当なメソッドからAsyncTaskを直接呼び出して、非同期処理を実行しています。これだとThis AsyncTask class should be static or leaks might occurというWarningが出てしまいました。

public class MainActivity extends AppCompatActivity {

    /* 非同期処理の呼び出し */
    public void asyncTaskSample() {
        new AsyncTask<Integer, Integer, Integer>() {
            @Override
            protected Integer doInBackground(Integer ...params) {
                System.out.println("Hello World!!");
                return 0;
            }
        }.execute(0);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        asyncTaskSample();
    }
}

このエラーの原因を他のサイトから引用します。

  • 非staticな内部クラスは暗黙に親オブジェクト(Activityオブジェクト)への参照を持っている。
  • AsyncTaskが内部的に利用しているスレッドが生きている限りこのオブジェクトがガベージコレクションの対象にならない。

うーん、よくわからない。

この記事にあるように、「非static内部クラスインスタンスが存在する = 外部クラスインスタンスが存在する」という状態のため、非static内部クラスは外部クラスを参照することができるようです。そして参照を持つオブジェクトはガベージコレクションの対象にならないため、非同期処理が終わってもメモリから解放されない。これがメモリリークの原因になってしまうようです。わかったようなわからないような…。

で、原因がわかったところで上のコードをどう直したらいいかと言うと、Warningの通りにAsyncTaskstaticなクラスにする、もしくは、別クラスにすればいいです。以下はstaticなクラスにした例。

public class MainActivity extends AppCompatActivity {

    /* 非同期処理の呼び出し */
    public void asyncTaskSample() {
        MyTask task = new MyTask();
        task.execute(0);
    }

    private static class MyTask extends AsyncTask<Integer, Integer, Integer> {
        @Override
        protected Integer doInBackground(Integer ...params) {
            System.out.println("Hello World!!");
            return 0;
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        asyncTaskSample();
    }
}

ただこれでもまだ問題があって、上のコードだとActivityオブジェクトの非staticな変数にアクセスできないです。上のコードでは実装してないけど、ViewとかをMyTaskからいじりたかったりします。そこらへんをどうやってActivityオブジェクトから非同期処理に渡すのが最適かいまいちわかりません。

この記事では、AsyncTaskImageViewを渡すのに、コンストラクタで渡しているのですが、AsyncTaskImageViewへの参照を持たないように、WeakReferenceというのを使っています。参考までに、コードを引用させてもらいます。

class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {  
    private final WeakReference<ImageView> mImageViewReference;  
    int mWidth;  
    int mHeight;  
    String mFilePath;  
  
    public BitmapWorkerTask(ImageView imageView) {  
        mImageViewReference = new WeakReference<ImageView>(imageView);  
        mWidth = imageView.getWidth();  
        mHeight = imageView.getHeight();  
    }  
  
    // バックグラウンドで画像をデコード  
    @Override  
    protected Bitmap doInBackground(String... params) {  
        mFilePath = params[0];  
        return decodeSampledBitmapFromFile(mFilePath, mWidth, mHeight);  
    }  
  
    // ImageView に Bitmap をセット  
    @Override  
    protected void onPostExecute(Bitmap bitmap) {  
        if (mImageViewReference != null && bitmap != null) {  
            final ImageView imageView = mImageViewReference.get();  
            if (imageView != null) {  
                imageView.setImageBitmap(bitmap);  
            }  
        }  
    }  
}

まとめ

結局、方法はいろいろあってどれが最適か正直わかりません。ただ明らかに間違っているコードもネットには散見されるので、注意が必要だなと感じました。(そう言いつつも自分の書いたコードが間違ってるかも...)AsyncTask奥深すぎる...

参考

staticって何?【Java】

この記事がめちゃくちゃわかりやすかった。
staticはクラスの変数、メソッドで、非staticはインスタンスの変数、メソッドということ。

JavaPythonに比べると難しいです。でも勉強になります。Javaって結構古い言語かと思っていたけど、Androidアプリ開発ではまだ結構使われているんですね。今後はKotlinに移行していきそうですけども。

参考

一番かんたんなJava入門