• トップ
  • ブログ一覧
  • NMFによる画像のノイズ除去【機械学習】
  • NMFによる画像のノイズ除去【機械学習】

    メディアチームメディアチーム
    2020.09.21

    IT技術

    NMF で画像ノイズが除去できる

    ノイズ画像とは、以下のように、ところどころの画素値が「0」になっている画像のことです。

    カメラの撮像素子の不良など原因は様々ですが、このノイズを除去する方法の1つに「NMF」があります。

    NMF でノイズを除去する

    こうした画像を、NMF を用いてノイズ除去すると、以下のような画像に補完することができます。

    NMF のノイズ除去法は、学習データセットの質の向上にも役立ちます。

    今回は、「NMF を用いたノイズ除去の手法」を学んでいきましょう!

    NMF とは

    NMF (Non-negative Matrix Factorization)とは、非負な行列分解のことです。

    非負行列分解が何かを学ぶ前に、まずは「行列分解」について解説していきます。

    行列分解とは?

    行列分解は、データの基底を求め、それを用いてデータを近似することです。

    基底を求めることで、「データにあまり影響のない基底は和に入れない」とすると、データの容量を下げることも可能です。

    正確にはフーリエ変換の範囲ですが、圧縮技術の分野で多く用いられている手法です。

    基底による画像近似の式

    さて、先ほどの例を「ベクトル」と「行列」の積で表現してみましょう。

    よって、「基底による画像の近似」は以下の式になることがわかりました。

    行列分解の式

    また、基底は1つの画像から抽出するものではなく、たくさんの画像から学習するものです。

    そのため、他にも N 個の画像があると考えると、一般的に「行列分解の式」は以下のようになります。

    このように、「行列を他の2つの行列で分解すること」を行列分解といいます。

    A は「基底の重み」で、それぞれの基底が各画像にどれだけ影響を及ぼすかを表します。

    また、P は「基底ベクトル」であり、変化するのは各画像に対するその重み行列 A だけです。

    非負行列分解とは?

    さて、ここでようやく「非負行列分解(NMF)」の話にいきましょう。

    非負行列分解はその名の通り、各行列が全て非負である条件をつけた行列分解となります。

    非負行列分解のメリット

    では、非負行列分解を使うメリットとは何でしょうか?

    例として、私たちが普段目にしている写真を考えてみましょう。

    写真には、存在している物体以外は写りません。

    つまり、実際の画像は「各画像、ひいては基底の足し算」でしかないのです。

    現実の画像において、負の重みは存在しないということですね。

    一方、NMF は非負である分、より画像の特徴を抽出することができるという点で優れているというわけです。

    NMF で画像のノイズを除去しよう

    それでは実際に、NMF で画像ノイズを除去し、補完画像を生成してみたいと思います。

    今回行った実験の理論は、以下のとおりです。

    NMF で基底を求める

    最初に、NMF を使って、ノイズでない画像データセットから基底を求めます。

    復元画像を基底で表す

    そうして、復元した穴ぬけ画像を、上記で求めた基底を用いて表していきます。

    これらの基底を用いて、ノイズ画像を近似する重み係数を上記の式に基づき、単純な勾配降下法で解いていきます。

    ノイズなしの基底で画像復元すればノイズを除去できるのか?

    この訓練画像から得られた基底は、ノイズを含まない基底です。

    そのため、「この基底を用いて画像復元を行えば、ノイズ除去できるのではないか?」というのが、この実験の目的です。

    python での NMF の学習法

    python で NMF をやる場合、以下のようなモデルを作れば簡単に学習ができます。

    1img_vec #訓練画像を一次元にしたデータのリスト ※今回の場合は(3000,100*100)
    2model = NMF(img_vec.shape, rank=n_components).cuda() #NMFモデルをGPUで使えるように
    3_,reconstruct = model.fit_transform(torch.from_numpy(img_vec).cuda(),l1_ratio=1) #このコードで実際に学習,reconstructが近似された画像

    「重み係数」と「基底」は、以下のように取り出すことができます。

    1P = model.W.detach().cpu().numpy() #重みを取り出してnumpyに変換
    2Q = model.H.detach().cpu().numpy() #基底を取り出してnumpyに変換

    コード解説

    では、実験コードを解説していきます。

    パラメータを初期化する

    推定するパラメータは、基底の数だけあります。

    その分のパラメータを初期化しておきましょう!

    1Q_image = Q.reshape(n_components,resize[0],resize[1]) # 基底を二次元に戻す
    2for i in range(Q_image.shape[0]): 
    3#   params.append(torch.zeros(1, requires_grad=True))

    重みと基底を使って画像を復元する

    初期値0の「重み」と「基底」を用いて、画像の復元を行います。

    最初は全ての画素値が「0」ですが、だんだん元画像に近似していきます。

    1y = 0 
    2for i in range(Q_image.shape[0]): 
    3  params[i] = torch.tensor(params[i],requires_grad=True) 
    4  y += torch.from_numpy(Q_image[i])*params[i]

    近似画像と元画像が近づくようにパラメータを更新する

    上記で近似画像「y」が求められました。

    後は、この近似した画像と元画像が近づくよう、パラメータの更新を行います。

    すると、簡単に最適な重みパラメータを求めることができます。

    1MSE = ((y - torch.from_numpy(image))**2).mean()
    2with torch.no_grad():
    3  MSE.backward()
    4  for i in range(Q_image.shape[0]):
    5    params[i] = params[i] - params[i].grad*learning_rate # 勾配降下法でパラメータを更新

    重みを使って近似画像を保存

    学習が終わったら、先ほど求めた重みを用いて、近似した画像を保存します。

    1y = 0
    2for i in range(Q_image.shape[0]):
    3  y += Q_image[i]*params[i].detach().numpy()*255
    4pil_img = Image.fromarray(y)
    5pil_img.convert("L").save("./drive/My Drive/result_NMF/predict.jpg","JPEG")

    本記事の序盤にも見せましたが、以下のような結果になるはずです。

    学習の際の注意点

    学習時に注意する点として、各重み係数のパラメータの初期値を「1」にしてしまうと、画像の復元結果が良くなりません

    原因は、 NMF がスパース性を持っているためです。

    有用な基底を用いて画像を表現できないので、無駄な基底も復元結果に影響を及ぼしてしまうというわけです。

    そのため初期値は「0」として、まず全ての基底は画像の生成に対して無意味であると仮定した上で、徐々に関係のある基底の重みを増やしていくことにしました。

    実験結果

    以下は、実際に NMF を用いて3000枚の画像で訓練した基底から、画像を復元した結果です。

    基底数と復元精度の関係

    基底の数に応じて、復元精度がどうなるか見てみましょう。

    基底の個数は、1000程度からは、増やしても画像の精度はあまり変わりませんでした。

    そのため、基底の個数は「1000」としました。

    基底の重みの分布

    続いて、基底の重みがどのぐらいの分布をしているか、可視化してみましょう!

    このグラフのように、ほとんどの基底の重みが「0」付近に分布していることが読み取れます。

    つまり、「非負」という制限をつけたことで、NMF は自ずとスパース性を持って学習できていることが読み取れます。

    これは、画像にとって意味のない基底は重みが「0」になるように学習できている証拠です。

    実験コード(Github)

    以下のリポジトリに NMF のコードが載っています。

    README にしたがって導入すると、NMF を実装できるので、ぜひ試してみてください!

    【Github ソースコード】
    https://github.com/rightcode/NMF

    さいごに

    今回は、NMF を用いて画像のノイズ除去を試してみました。

    画像ノイズの除去は長い歴史がありますが、その中でも NMF は結構古めの手法です。

    次は、「Deep Image prior」を使ってノイズ除去してみたいと思います!

    こちらの記事もオススメ!

    featureImg2020.07.28機械学習 特集知識編人工知能・機械学習でよく使われるワード徹底まとめ!機械学習の元祖「パーセプトロン」とは?【人工知能】ニューラルネ...

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background