TensorFlow QAT:量子化によるTensorFlowモデルの高速化と効率化

はじめに:TensorFlowと量子化

TensorFlowは、Googleが開発したオープンソースの機械学習フレームワークであり、画像認識、自然言語処理、音声認識など、様々な分野で広く利用されています。深層学習モデルの開発からデプロイまで、一貫したワークフローを提供し、研究者や開発者にとって不可欠なツールとなっています。

近年、機械学習モデルはますます複雑化し、計算リソースの消費量が増加しています。特に、エッジデバイスやモバイルデバイスなど、計算能力やバッテリー容量が限られた環境での利用においては、モデルの効率化が重要な課題となります。

そこで注目されているのが量子化 (Quantization) です。量子化とは、モデルの重みや活性化関数の精度を低くすることで、モデルサイズを削減し、推論速度を向上させる技術です。通常、深層学習モデルのパラメータは32ビット浮動小数点数(FP32)で表現されますが、量子化によって、例えば8ビット整数(INT8)などの低精度な表現に変換します。

量子化によって、モデルサイズを削減できるだけでなく、必要なメモリ帯域幅も削減できるため、推論速度が向上し、エネルギー効率も改善されます。これは、特にリソース制約のある環境において大きなメリットとなります。

TensorFlowは、量子化をサポートする様々なツールを提供しており、開発者は容易に量子化を適用することができます。その中でも、QAT(Quantization Aware Training) は、学習時に量子化を考慮することで、精度劣化を最小限に抑えながら量子化を行うことができる強力な手法です。

本記事では、TensorFlowにおけるQATについて、その概要、メリット・デメリット、実装方法、応用事例などを詳しく解説します。

QAT(Quantization Aware Training)とは

QAT(Quantization Aware Training)、すなわち量子化対応学習とは、学習段階から量子化をシミュレートすることで、量子化後のモデル精度低下を抑制する手法です。 post-training quantization (PTQ) と比較して、一般的に高い精度を維持できるため、より高度な量子化手法として知られています。

従来の量子化手法(Post-Training Quantization:PTQ)は、学習済みのモデルに対して量子化を行うため、量子化によって精度が大きく低下する場合があります。これは、量子化によってモデルの重みや活性化関数の値が離散化されるため、学習時に想定されていた連続的な値とのずれが生じるためです。

QATでは、学習中に**「量子化ノード」** を挿入し、forward pass時に量子化と逆量子化をシミュレートします。つまり、疑似的に量子化された値を用いて学習を行うことで、量子化後の精度低下を考慮した学習が可能になります。

具体的には、以下のステップで進められます。

  1. モデルの準備: 通常の浮動小数点数 (FP32) で学習されたモデル、もしくは新たにFP32で設計されたモデルを準備します。
  2. 量子化ノードの挿入: モデルの重みや活性化関数に対して、量子化と逆量子化をシミュレートする「量子化ノード」を挿入します。
  3. 学習の実施: 量子化ノードを挿入したモデルに対して、通常の学習と同様に、データセットを用いて学習を行います。この時、量子化ノードによって量子化と逆量子化がシミュレートされます。
  4. 量子化モデルの作成: 学習終了後、量子化ノードを取り除き、実際の量子化された重みを持つモデルを作成します。

QATの重要なポイントは、量子化による影響を学習中に考慮する ことです。これにより、モデルは量子化された状態でも高い精度を維持できるように適応します。学習時における量子化のシミュレーションによって、量子化による精度低下を最小限に抑え、より効率的な推論を実現することが可能になります。

QATのメリットとデメリット

QAT(Quantization Aware Training)は、量子化による精度低下を抑制する強力な手法ですが、いくつかのメリットとデメリットが存在します。

メリット:

  • 高い精度維持: 最も大きなメリットは、量子化による精度低下を大幅に抑制できる点です。学習時に量子化をシミュレートすることで、モデルは量子化された状態に適応し、Post-Training Quantization(PTQ)よりも高い精度を維持できます。
  • 広い適用範囲: 比較的広い範囲のモデルアーキテクチャやタスクに適用可能です。画像認識、自然言語処理など、様々な分野のモデルで有効性が確認されています。
  • モデルの最適化: QATを通して、量子化に適したモデル構造を学習できる可能性があります。これにより、推論速度の向上や省メモリ化の効果を最大限に引き出すことができます。
  • ハードウェアアクセラレーションの活用: 量子化されたモデルは、多くのハードウェアプラットフォームで効率的に実行できます。特に、量子化演算をハードウェアレベルでサポートするデバイス(例:TensorFlow Lite Micro)では、大幅な高速化が期待できます。

デメリット:

  • 学習コストの増加: QATは、PTQと比較して学習コストが高くなります。量子化ノードの挿入や、量子化を考慮した学習を行う必要があるため、学習時間や計算リソースが増加する可能性があります。
  • 実装の複雑さ: PTQに比べて実装が複雑になります。量子化ノードの挿入、量子化範囲の調整、学習戦略の検討など、様々な要素を考慮する必要があります。
  • ハイパーパラメータ調整の複雑さ: 量子化に関連するハイパーパラメータ(例:量子化ビット数、量子化範囲)の調整が必要となる場合があります。これらのハイパーパラメータは、モデルの精度や推論速度に影響を与えるため、慎重な調整が必要です。
  • 特定のハードウェア依存性: 量子化後のモデルは、特定のハードウェアプラットフォームに最適化される場合があります。異なるハードウェア環境で実行する場合は、再度調整が必要となる可能性があります。

まとめ:

QATは、高い精度維持と広い適用範囲を持つ強力な量子化手法ですが、学習コストの増加や実装の複雑さなどのデメリットも存在します。モデルの精度要件、計算リソース、開発期間などを考慮して、QATを適用するかどうかを慎重に判断する必要があります。多くの場合、高い精度が求められる場合や、リソース制約が厳しい環境において、QATは有効な選択肢となります。

TensorFlowにおけるQATの実装

TensorFlowでQAT(Quantization Aware Training)を実装するには、いくつかの方法があります。ここでは、TensorFlow Model Optimization Toolkitを使用する方法を基本として解説します。

1. TensorFlow Model Optimization Toolkit のインストール:

まず、TensorFlow Model Optimization Toolkit をインストールします。

pip install -q tensorflow-model-optimization

2. モデルの準備:

QATを適用するモデルを準備します。Keras Sequentialモデル、Functional APIモデル、またはサブクラス化モデルを使用できます。ここでは、例としてKeras Sequentialモデルを使用します。

import tensorflow as tf
from tensorflow import keras

# MNISTデータセットをロード
(train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()

# データの正規化
train_images = train_images.astype('float32') / 255.0
test_images = test_images.astype('float32') / 255.0

# モデルの定義
model = keras.Sequential([
  keras.layers.InputLayer(input_shape=(28, 28)),
  keras.layers.Reshape(target_shape=(28, 28, 1)),
  keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu'),
  keras.layers.MaxPooling2D(pool_size=(2, 2)),
  keras.layers.Conv2D(64, kernel_size=(3, 3), activation='relu'),
  keras.layers.MaxPooling2D(pool_size=(2, 2)),
  keras.layers.Flatten(),
  keras.layers.Dense(10, activation='softmax')
])

3. モデルの準備 (QAT用):

TensorFlow Model Optimization ToolkitのAPIを使用して、QATに対応したモデルに変換します。tfmot.quantization.keras.quantize_model 関数を使用します。

import tensorflow_model_optimization as tfmot

quantize_model = tfmot.quantization.keras.quantize_model

# QATに対応したモデルに変換
quantized_model = quantize_model(model)

# コンパイル
quantized_model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['accuracy'])

quantized_model.summary()

この段階で、モデルの各層にQuantizeWrapperV2という層が追加され、量子化の準備が整います。

4. QATの実行:

変換されたモデルを使用して学習を行います。

# 学習
quantized_model.fit(train_images, train_labels, epochs=1, validation_split=0.1)

5. 量子化モデルの作成:

学習後、量子化されたモデルを作成します。tfmot.quantization.keras.strip_model 関数を使用して、学習済みの量子化情報を適用したモデルを作成します。

# 量子化モデルの作成
final_model = tfmot.quantization.keras.strip_model(quantized_model)

# モデルの保存
final_model.save('quantized_mnist_model.h5')

6. モデルの評価:

量子化されたモデルを評価し、精度を確認します。

_, quantized_keras_file_accuracy = final_model.evaluate(
       test_images, test_labels, verbose=0)

print('Quantized Keras model accuracy:', quantized_keras_file_accuracy)

7. TFLiteモデルへの変換 (オプション):

量子化されたモデルを TensorFlow Lite 形式に変換して、エッジデバイスなどで実行できるようにします。

converter = tf.lite.TFLiteConverter.from_keras_model(final_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
quantized_tflite_model = converter.convert()

# TFLiteモデルの保存
open("quantized_mnist_model.tflite", "wb").write(quantized_tflite_model)

補足:

  • カスタマイズ: tfmot.quantization.keras.quantize_annotate_layer などを利用して、特定のレイヤーだけ量子化するなど、より細かな制御が可能です。
  • コールバック: 学習中に精度をモニタリングするために、tfmot.sparsity.keras.UpdatePruningStep などのコールバックを使用できます。
  • より高度な設定: 量子化方式(例:MinMax、MovingAverage)、量子化ビット数など、様々な設定を調整することで、精度とパフォーマンスのバランスを最適化できます。

この手順は、TensorFlowでQATを実装するための基本的な流れを示しています。より詳細な情報や高度な設定については、TensorFlow Model Optimization Toolkit のドキュメントを参照してください。

QATの具体的な手順:モデルの準備から量子化モデルの作成まで

QAT(Quantization Aware Training)を実装するための具体的な手順を、モデルの準備から量子化モデルの作成まで詳細に説明します。

1. モデルの準備:

  • データセットの準備: 学習に使用するデータセットを準備します。画像認識であれば、MNIST、CIFAR-10、ImageNetなど、タスクに適したデータセットを選択します。自然言語処理であれば、テキストデータセットを準備します。
  • モデルアーキテクチャの選択: 適切なモデルアーキテクチャを選択します。画像認識であれば、ResNet、MobileNet、EfficientNetなど、自然言語処理であれば、Transformer、BERTなど、タスクとリソース制約に応じて選択します。
  • モデルの定義: 選択したモデルアーキテクチャに基づいて、TensorFlow/Kerasでモデルを定義します。必要に応じて、学習済みモデルをロードしてファインチューニングすることも可能です。
import tensorflow as tf
from tensorflow import keras

# MNISTデータセットをロード
(train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()

# データの正規化
train_images = train_images.astype('float32') / 255.0
test_images = test_images.astype('float32') / 255.0

# モデルの定義
model = keras.Sequential([
  keras.layers.InputLayer(input_shape=(28, 28)),
  keras.layers.Reshape(target_shape=(28, 28, 1)),
  keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu'),
  keras.layers.MaxPooling2D(pool_size=(2, 2)),
  keras.layers.Conv2D(64, kernel_size=(3, 3), activation='relu'),
  keras.layers.MaxPooling2D(pool_size=(2, 2)),
  keras.layers.Flatten(),
  keras.layers.Dense(10, activation='softmax')
])

2. QATに対応したモデルへの変換:

  • TensorFlow Model Optimization Toolkitのインポート: 必要なライブラリをインポートします。
import tensorflow_model_optimization as tfmot
  • モデルの量子化: tfmot.quantization.keras.quantize_model 関数を使用して、モデルを量子化します。
quantize_model = tfmot.quantization.keras.quantize_model
quantized_model = quantize_model(model)

このステップで、モデルの各層に量子化ノードが挿入されます。

3. 学習の設定:

  • コンパイル: モデルをコンパイルします。損失関数、最適化アルゴリズム、評価指標などを設定します。量子化モデルに適した学習率やバッチサイズを検討します。
quantized_model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['accuracy'])
  • コールバックの設定 (推奨): 学習の進捗をモニタリングし、早期停止やモデルの保存などを行うためのコールバックを設定します。tf.keras.callbacks.ModelCheckpointtf.keras.callbacks.EarlyStopping などを利用します。
model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
    filepath='qat_model_checkpoint',
    save_weights_only=True,
    monitor='val_accuracy',
    mode='max',
    save_best_only=True)

4. QATの実行:

  • 学習の開始: 量子化されたモデルを学習します。学習データセットを使用し、エポック数やバッチサイズなどを調整します。
quantized_model.fit(train_images, train_labels, epochs=10, validation_split=0.1, callbacks=[model_checkpoint_callback])

5. 量子化モデルの作成:

  • 量子化モデルの抽出: 学習済みの量子化モデルから、量子化された重みを持つモデルを作成します。 tfmot.quantization.keras.strip_model 関数を使用します。
final_model = tfmot.quantization.keras.strip_model(quantized_model)

6. モデルの評価:

  • 精度の評価: 量子化されたモデルの精度を評価します。テストデータセットを使用し、損失、精度、その他の評価指標を計算します。
_, quantized_keras_file_accuracy = final_model.evaluate(
       test_images, test_labels, verbose=0)

print('Quantized Keras model accuracy:', quantized_keras_file_accuracy)

7. TFLiteモデルへの変換 (オプション):

  • TFLiteコンバーターの作成: TensorFlow Liteコンバーターを作成します。
  • 最適化オプションの設定: 量子化オプションを設定します (tf.lite.Optimize.DEFAULT)。
  • モデルの変換: モデルをTensorFlow Lite形式に変換します。
  • モデルの保存: 変換されたモデルをファイルに保存します。
converter = tf.lite.TFLiteConverter.from_keras_model(final_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
quantized_tflite_model = converter.convert()

open("quantized_mnist_model.tflite", "wb").write(quantized_tflite_model)

8. デプロイメント:

  • エッジデバイスへのデプロイ: 量子化されたモデルを、エッジデバイスやモバイルデバイスなどのリソース制約のある環境にデプロイします。TensorFlow Liteインタープリターを使用して、モデルを実行します。

これらの手順に従うことで、TensorFlowでQATを実装し、量子化されたモデルを作成できます。必要に応じて、ハイパーパラメータや学習戦略を調整し、最適な精度とパフォーマンスを実現してください。

QAT適用時の注意点とトラブルシューティング

QAT(Quantization Aware Training)は、量子化による精度低下を抑える効果的な手法ですが、適用にあたってはいくつかの注意点と、発生しうる問題に対するトラブルシューティングが必要です。

注意点:

  • 学習率の調整: QATでは、量子化ノードの導入により、学習の安定性が損なわれる場合があります。そのため、学習率を通常よりも小さく設定したり、学習率スケジューラを導入したりするなど、学習率の調整が必要となる場合があります。
  • 量子化範囲の調整: 量子化に使用する範囲(scaleとzero_point)は、モデルの性能に大きな影響を与えます。適切な範囲を設定するために、calibration datasetを使用したり、実験的に範囲を調整したりする必要があります。特に、ReLU6のような有界な活性化関数を使用する場合は、範囲を適切に設定することが重要です。
  • バッチ正規化層の扱い: バッチ正規化層は、学習中に統計量を更新するため、量子化との相性が悪い場合があります。バッチ正規化層を融合(fuse)したり、量子化ノードを調整したりすることで、精度を改善できる場合があります。
  • レイヤーの順序: 量子化ノードを挿入するレイヤーの順序も重要です。一般的には、畳み込み層や全結合層の前に挿入するのが適切ですが、モデルの構造によっては、異なる順序がより良い結果をもたらす場合もあります。
  • 学習時間の増加: QATは、通常の学習よりも時間がかかる場合があります。特に、モデルが複雑な場合や、データセットが大きい場合は、学習時間が大幅に増加する可能性があります。
  • オーバーフィッティング: QATは、量子化を考慮した学習を行うため、通常の学習よりもオーバーフィッティングしやすくなる場合があります。正則化手法(例:ドロップアウト、L1/L2正則化)を適切に適用することで、オーバーフィッティングを抑制する必要があります。

トラブルシューティング:

  • 精度低下: QATを適用したにも関わらず、精度が低下する場合は、以下の点を確認してください。

    • 学習率が適切か
    • 量子化範囲が適切か
    • バッチ正規化層の扱いが適切か
    • 学習データが十分に用意されているか
    • オーバーフィッティングしていないか
  • 学習が不安定: 学習中に損失が発散したり、精度が変動したりする場合は、以下の点を確認してください。

    • 学習率が大きすぎないか
    • 勾配クリッピングを適用しているか
    • バッチサイズが適切か
    • 正則化が適切に適用されているか
  • メモリ不足: QATでは、量子化ノードの導入により、メモリ使用量が増加する場合があります。メモリ不足が発生する場合は、バッチサイズを小さくしたり、モデルを分割したり、メモリ使用量を削減する手法(例:勾配蓄積)を適用したりする必要があります。
  • TensorFlowのバージョン: TensorFlow Model Optimization Toolkitは、特定のTensorFlowバージョンでのみ動作する場合があります。互換性のあるバージョンを使用しているか確認してください。
  • エラーメッセージ: TensorFlowから出力されるエラーメッセージは、問題解決の重要な手がかりとなります。エラーメッセージをよく読み、原因を特定するように努めてください。

具体的なトラブルシューティング例:

  • QAT適用後に精度が大幅に低下した場合:

    • 量子化範囲を確認し、データセットの最大値と最小値をカバーするように調整します。
    • 学習率を小さくして、学習を再度実行します。
    • バッチ正規化層が適切に処理されているか確認します。
  • 学習中にNaNが発生する場合:

    • 学習率をさらに小さくします。
    • 勾配クリッピングを適用します。
    • 入力データにNaNが含まれていないか確認します。

QATは、モデルの精度と効率のバランスを取るための強力なツールですが、適切な設定とトラブルシューティングが必要です。上記の情報が、QAT適用時の問題解決に役立つことを願っています。

QATの応用事例

QAT(Quantization Aware Training)は、その高い精度維持能力から、様々な分野で応用されています。ここでは、具体的な応用事例をいくつか紹介します。

1. モバイルデバイス向け画像認識:

  • 概要: スマートフォンなどのモバイルデバイスでは、計算リソースやバッテリー容量に制約があります。QATを適用することで、画像認識モデルのサイズを削減し、推論速度を向上させることができます。これにより、リアルタイムな画像認識アプリケーション(例:物体検出、顔認識)を、低消費電力で実行することが可能になります。
  • 具体的な例:

    • Google Pixel Visual Core: GoogleのPixelスマートフォンに搭載された画像処理専用チップで、QATを用いて最適化されたモデルを使用し、高品質な画像処理を低消費電力で実現しています。
    • TensorFlow Lite: TensorFlow Liteは、モバイルデバイスやエッジデバイス向けの軽量な推論フレームワークです。QATで学習されたモデルをTensorFlow Lite形式に変換することで、モバイルアプリケーションに容易に組み込むことができます。

2. エッジデバイス向け音声認識:

  • 概要: スマートスピーカーやIoTデバイスなどのエッジデバイスでは、クラウドに接続せずに、ローカルで音声認識を行うことが求められます。QATを適用することで、音声認識モデルのサイズを削減し、推論速度を向上させることができます。これにより、プライバシー保護や低遅延な音声認識アプリケーションを実現できます。
  • 具体的な例:

    • マイクロコントローラーへの実装: QATを適用することで、非常に小さなマイクロコントローラーでも動作する音声認識モデルを作成できます。これにより、ウェアラブルデバイスや組み込みシステムなど、幅広いアプリケーションに音声認識機能を搭載できます。
    • キーワード認識: QATを用いて最適化されたキーワード認識モデルは、低消費電力で常にリスニング状態を維持し、特定のキーワードが検出された場合にのみ、より複雑な音声認識処理を開始することができます。

3. 自動運転:

  • 概要: 自動運転システムでは、リアルタイムな物体検出や経路計画が不可欠です。QATを適用することで、これらのタスクに使用される深層学習モデルの推論速度を向上させることができます。これにより、より安全で信頼性の高い自動運転システムを実現できます。
  • 具体的な例:

    • 物体検出モデルの高速化: QATを適用することで、LiDARやカメラからの入力に基づいて、歩行者、車両、標識などを検出する物体検出モデルの推論速度を向上させることができます。
    • 経路計画モデルの効率化: QATを適用することで、道路状況や交通状況に基づいて、最適な経路を計算する経路計画モデルの計算コストを削減できます。

4. 医療画像診断:

  • 概要: 医療画像診断では、CTスキャンやMRIなどの画像から、病変や異常を検出する必要があります。QATを適用することで、これらのタスクに使用される深層学習モデルの推論速度を向上させることができます。これにより、診断時間の短縮や、医療現場でのリアルタイムな診断支援が可能になります。
  • 具体的な例:

    • 腫瘍検出: QATを適用することで、医療画像から腫瘍を検出するモデルの推論速度を向上させることができます。これにより、放射線科医の診断を支援し、診断精度を向上させることができます。
    • 疾患分類: QATを適用することで、医療画像から疾患を分類するモデルの推論速度を向上させることができます。これにより、大規模な医療画像データを迅速に分析し、早期診断を支援することができます。

5. その他:

  • 自然言語処理: QATを適用することで、翻訳、テキスト要約、感情分析などの自然言語処理タスクに使用されるモデルの効率を向上させることができます。
  • 金融: QATを適用することで、不正検出、リスク評価、予測モデリングなどの金融タスクに使用されるモデルの効率を向上させることができます。

これらの例からもわかるように、QATは、様々な分野で機械学習モデルの効率化に貢献し、より高度なアプリケーションの実現を可能にしています。今後、QAT技術はさらに発展し、より幅広い分野で応用されることが期待されます。

まとめ:TensorFlow QATの今後の展望

TensorFlow QAT(Quantization Aware Training)は、量子化による精度低下を最小限に抑えつつ、モデルのサイズ削減と推論速度の向上を実現する強力な手法です。特に、リソース制約のあるエッジデバイスやモバイルデバイスでの利用において、その有効性が高く評価されています。

QATの重要性とメリット:

  • 精度維持: 量子化による精度劣化を抑制し、実用的な精度を維持したままモデルの効率化を実現します。
  • 幅広い応用: 画像認識、自然言語処理、音声認識など、様々な分野のモデルに適用可能です。
  • ハードウェアアクセラレーション: 量子化されたモデルは、特定のハードウェアで効率的に実行でき、高速化と省電力化に貢献します。

今後の展望:

TensorFlow QATは、現在も活発に研究開発が進められており、今後以下の点が強化されると予想されます。

  • 自動化の推進: モデルの量子化プロセスをより自動化し、専門知識がないユーザーでも容易にQATを適用できるようにする。例えば、モデル構造を解析し、最適な量子化設定を自動的に決定する機能などが期待されます。
  • 量子化手法の多様化: INT8だけでなく、INT4やバイナリ量子化など、より極端な量子化手法への対応を進め、さらなるモデルサイズ削減と高速化を目指す。
  • 学習効率の向上: QATにおける学習コストを削減するための研究が進められる。例えば、より少ないデータで効果的な学習を行う手法や、学習時間を短縮する最適化手法の開発などが期待されます。
  • ハードウェアとの連携強化: 特定のハードウェアアーキテクチャに最適化されたQATツールを開発し、ハードウェアアクセラレーションの効果を最大限に引き出す。
  • 量子化シミュレーションの高度化: より高精度な量子化シミュレーション技術を開発し、実際のハードウェア上での性能を正確に予測できるようにする。これにより、量子化後のモデルの性能を事前に評価し、最適な量子化設定を選択することが可能になります。
  • 動的量子化への対応: 入力データに応じて動的に量子化範囲を調整する動的量子化技術をQATに組み込むことで、より柔軟で高性能な量子化モデルを実現する。
  • 量子化と他の最適化手法との組み合わせ: プルーニングや蒸留など、他のモデル最適化手法とQATを組み合わせることで、さらなる性能向上を目指す。
  • エッジAIプラットフォームとの統合: QATで最適化されたモデルを、様々なエッジAIプラットフォームに容易にデプロイできる環境を整備する。

TensorFlow QATは、機械学習モデルの効率化における重要な技術であり、今後の発展によって、より多くのデバイスで高度なAIアプリケーションが利用できるようになると期待されます。AI技術の普及と進化に貢献する、その将来性が非常に楽しみです。

Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です