<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Copyright 2019 The TensorFlow Neural Structured Learning Authors
Step1:  画像分類のための敵対的正則化
<table class="tfo-notebook-buttons" align="left">
  <td><a target="_blank" href="https
Step2:  ライブラリをインポートします。neural_structured_learning を nsl と略します。
Step3:  ハイパーパラメータ
モデルのトレーニングと評価のための（HParams オブジェクト内の）ハイパーパラメータを集めて説明します。
入力/出力：
input_shape：入力テンソルの形状。各画像は 28×28 ピクセルで 1 チャンネルです。
num_classes：[0-9] に対応する数字 10 個分があり、合計 10 クラスです。
モデルアーキテクチャ：
conv_filters：各畳み込みレイヤーのフィルタ数を指定する数値のリスト。
kernel_size：すべての畳み込みレイヤーで共有する 2 次元の畳み込みウィンドウのサイズ。
pool_size：各最大 Pooling レイヤーで画像をダウンスケールするための係数。
num_fc_units：完全に接続された各レイヤーの単位（すなわち幅）の数。
トレーニングと評価：
batch_size：トレーニングや評価に使用するバッチサイズ。
epochs：トレーニングのエポック数。
敵対的学習：
adv_multiplier：ラベル付けされた損失に対して相対的な、トレーニング目的内の敵対的損失の重み。
adv_step_size：敵対的摂動の大きさ。
adv_grad_norm：敵対的摂動の大きさを測るノルム。
Step4:  MNIST データセット
MNIST データセットには手書きの数字（ '0' から '9' まで）のグレースケール画像が含まれています。各画像は低解像度（28×28 ピクセル）で数字 1 文字を示しています。このタスクでは、画像を数字ごとに 1 つずつ、10 のカテゴリに分類します。
ここで TensorFlow Datasets から MNIST データセットを読み込みます。これはデータのダウンロードと tf.data.Dataset の構築処理をします。読み込んだデータセットには 2 つのサブセットがあります。
6 万個の例を含む train
1 万個の例を含む test
両方のサブセットに含まれる例は、以下の 2 つのキーを持つ特徴ディクショナリに格納されています。
image：0 から 255 までのピクセル値の配列。
label：0 から 9までの真の正解ラベル。
Step5:  モデルを数値的に安定させるには、normalize 関数にデータセットをマッピングし、ピクセル値を [0, 1] に正規化します。トレーニングセットをシャッフルしてバッチ処理を行った後、基本モデルをトレーニングするために例を特徴タプル (image, label) に変換します。また、後で使用できるようにタプルをディクショナリに変換する関数も用意しています。
Step7:  基本モデル
ここでの基本モデルは、3 つの畳み込みレイヤーと 2 つの全結合レイヤー（HPARAMS の定義）で構成されるニューラルネットワークです。ここでは、Kerasu の Functional API を使用して定義しています。他の API やモデルアーキテクチャ（サブクラス化など）を自由に試してみてください。NSL フレームワークでは 3 つすべての Keras API をサポートしていることに注意してください。
Step8:  次に、基本モデルのトレーニングと評価を行います。
Step9:  基本モデルがテストセットで 99% の精度を達成していることが分かります。下記の敵対的摂動下でのロバスト性では、モデルがどれだけロバストであるかを確認します。
敵対的正則化モデル
ここでは、NSL フレームワークを使用して、数行のコードで Keras モデルに敵対的トレーニングを組み込む方法を示します。基本モデルをラップして、トレーニング目的に敵対的正則化を含む新しい tf.Keras.Model を作成します。
まず最初に、ヘルパー関数 nsl.configs.make_adv_reg_config を使用して、関連するすべてのハイパーパラメータを含む構成オブジェクトを作成します。
Step10:  これで AdversarialRegularization を使って基本モデルをラップすることができます。ここでは、既存の基本モデル（base_model）を後で比較に使用できるように、新しい基本モデル（base_adv_model）を作成します。
返される adv_model は tf.keras.Model のオブジェクトであり、そのトレーニング目的には敵対的損失の正則化項を含みます。この損失を計算するためには、モデルは通常の正規入力（特徴 image）に加えてラベル情報（特徴 label）にアクセスする必要があります。この理由から、データセットの例は変換してタプルからディクショナリに戻します。そして label_keys パラメータを介してどの特徴がラベル情報を含んでいるかをモデルに伝えます。
Step11:  次に、敵対的正則化モデルをコンパイルしてトレーニングし、評価します。「損失ディクショナリに出力がありません」というような警告が出るかもしれませんが、これは adv_model が基本実装に依存せずトータルの損失を計算しているため、問題ありません。
Step12:  敵対的正則化モデルもまた、テストセットで非常に優れた性能（99% の精度）を達成していることが分かります。
敵対的摂動下におけるロバスト性
ここでは基本モデルと敵対的正則化モデルの、敵対的摂動下におけるロバスト性を比較します。
敵対的摂動の例の生成には AdversarialRegularization.perturb_on_batch 関数を使用します。そして、これは基本モデルを基にして生成します。これを行うためには、AdversarialRegularization を使用して基本モデルをラップします。トレーニング（Model.fit）を呼び出さない限り、モデル内の学習変数に変更はなく、モデルは基本モデルのセクションのものと同じであることに注意してください。
Step13:  評価対象のモデルをディクショナリに収集し、各モデルのメトリックオブジェクトを作成します。
基本モデルと同じ（ラベル情報が不要な）入力形式を持つために adv_model.base_model を取る必要があることに注意してください。adv_model.base_model で学習する変数は、adv_model で学習する変数と同じです。
Step14:  摂動された例を生成し、それを用いてモデルを評価するループをここに示します。摂動された画像、ラベル、予測は次のセクションで可視化するために保存しておきます。
Step15:  入力が敵対的に摂動されると、基本モデルの精度が劇的に（99% から約 50% に）低下することが分かります。一方で、敵対的正則化されたモデルの精度低下はごくわずか（99% から 95% に）です。これは、敵対的学習がモデルのロバスト性向上に有効であることを示しています。
敵対的摂動された画像の例
ここで、敵対的摂動された画像を確認してみます。摂動された画像は人間が認識可能な数字を表示していますが、基本モデルをうまく騙せることが分かります。
<ASSISTANT_TASK:>
Python Code:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Explanation: Copyright 2019 The TensorFlow Neural Structured Learning Authors
End of explanation
!pip install --quiet neural-structured-learning
Explanation: 画像分類のための敵対的正則化
<table class="tfo-notebook-buttons" align="left">
  <td><a target="_blank" href="https://www.tensorflow.org/neural_structured_learning/tutorials/adversarial_keras_cnn_mnist"><img src="https://www.tensorflow.org/images/tf_logo_32px.png"> TensorFlow.orgで表示</a></td>
  <td><a target="_blank" href="https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/neural_structured_learning/tutorials/adversarial_keras_cnn_mnist.ipynb"><img src="https://www.tensorflow.org/images/colab_logo_32px.png"> Google Colab で実行</a></td>
  <td><a target="_blank" href="https://github.com/tensorflow/docs-l10n/blob/master/site/ja/neural_structured_learning/tutorials/adversarial_keras_cnn_mnist.ipynb"><img src="https://www.tensorflow.org/images/GitHub-Mark-32px.png">GitHub でソースを表示{</a></td>
  <td><a href="https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/neural_structured_learning/tutorials/adversarial_keras_cnn_mnist.ipynb"><img src="https://www.tensorflow.org/images/download_logo_32px.png">ノートブックをダウンロード</a></td>
</table>
概要
このチュートリアルでは、Neural Structured Learning（NSL）フレームワークを用いた画像分類のための敵対的学習（Goodfellow et al.、2014）の使用について説明します。
敵対的学習の中核となる考え方は、有機的な学習データに加え、敵対的なデータ（敵対的サンプルと呼ばれる）を用いてモデルをトレーニングすることです。これらの敵対的サンプルは人間の目には元のデータと同じように見えますが、摂動がモデルを混乱させ、誤った予測や分類が行われる原因となります。敵対的サンプルはモデルに誤った予測や分類を行わせ、誤認識させるように意図的に構成されています。このような例を用いてトレーニングを行うことによって、モデルは予測を行う際に敵対的摂動に対してロバストになるよう学習します。
このチュートリアルでは、Neural Structured Learning のフレームワークを使用し、ロバストなモデルを得るために敵対的学習を適用する手順を以下に説明します。
基本モデルとしてニューラルネットワークを作成します。このチュートリアルでは、tf.keras Functional API で基本モデルを作成します。この手順は、tf.keras Sequential API および Subclassing API で作成されたモデルと互換性があります。TensorFlow における Keras モデルの詳細については、こちらのドキュメントをご覧ください。
NSL フレームワークが提供するラッパークラス AdversarialRegularization で基本モデルをラップして、新しい tf.keras.Model インスタンスを作成します。この新しいモデルには、トレーニング目的の正則化項として敵対的損失が含まれます。
トレーニングデータの例を特徴ディクショナリに変換します。
新しいモデルをトレーニングして評価します。
初心者のための概要
画像分類の敵対的学習に関するビデオによる説明は「TensorFlow Neural Structured Leaning」YouTubeシリーズにあります。以下では、このビデオで説明されている重要な概念について、上記の「概要」セクションの説明を広げながらまとめています。
NSL フレームワークは、ニューラルネットワークによる学習を改善できるように、画像特徴量と構造化シグナルを同時に最適化します。ただし、ニューラルネットワークをトレーニングするために使用できる明示的な構造がない場合はどうでしょうか。このチュートリアルでは、構造を動的に作成する敵対的近傍値の作成（元のサンプルから変更されたもの）を伴う 1 つのアプローチを説明します。
まず、敵対的近傍値は、ニューラルネットを不正確な分類を出力させるように導く小さな摂動が適用された変更バージョンのサンプル画像として定義されます。これらの慎重に設計された摂動は通常、逆勾配方向に基づいており、トレーニング中のニューラルネットを混乱させることが意図されています。人間はサンプル画像と生成された敵対的近傍値を見分けることはできませんが、ニューラルネットにおいては、適用された摂動によって不正確な結論が有効に導びかれます。
生成された敵対的近傍値はサンプルに接続されるため、エッジごとに動的に構造が作成されます。この接続を使用して、ニューラルネットは、誤分類による混乱を避けて、全体的なニューラルネットワークの品質と精度を改善しながら、サンプルと敵対的近傍値の類似性を維持するよう学習します。
以下のコードセグメントはこの手順の概要ではありますが、以降のチュートリアルでは、より詳細に技術面を説明しています。
データを読み取って準備します。MNIST データセットを読み込み、特徴量値が [0,1] の範囲に収まるように正規化します。
```
import neural_structured_learning as nsl
(x_train, y_train), (x_train, y_train) = tf.keras.datasets.mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
```
ニューラルネットワークを構築します。この例では、Sequential Keras ベースモデルを使用します。
model = tf.keras.Sequential(...)
敵対的モデルを構成します。敵対的正則化に適用される乗数、経験的に選択されたステップサイズ/学習率の異なる値などのハイパーパラメータを含みます。構築されたニューラルネットワークのラッパークラスを使用して、敵対的正則化を呼び出します。
adv_config = nsl.configs.make_adv_reg_config(multiplier=0.2, adv_step_size=0.05)
adv_model = nsl.keras.AdversarialRegularization(model, adv_config)
標準 Keras ワークフロー（コンパイル、適合、評価）で終了します。
adv_model.compile(optimizer='adam', loss='sparse_categorizal_crossentropy', metrics=['accuracy'])
adv_model.fit({'feature': x_train, 'label': y_train}, epochs=5)
adv_model.evaluate({'feature': x_test, 'label': y_test})
ここでは、2 つの手順と 3 行の単純なコードで敵対的学習が有効化されているのがわかります。これが、ニューラル構造化学習フレームワークの持つ単純さです。以下のセクションでは、この手順をさらに説明します。
セットアップ
Neural Structured Learning パッケージをインストールします。
End of explanation
import matplotlib.pyplot as plt
import neural_structured_learning as nsl
import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds
Explanation: ライブラリをインポートします。neural_structured_learning を nsl と略します。
End of explanation
class HParams(object):
  def __init__(self):
    self.input_shape = [28, 28, 1]
    self.num_classes = 10
    self.conv_filters = [32, 64, 64]
    self.kernel_size = (3, 3)
    self.pool_size = (2, 2)
    self.num_fc_units = [64]
    self.batch_size = 32
    self.epochs = 5
    self.adv_multiplier = 0.2
    self.adv_step_size = 0.2
    self.adv_grad_norm = 'infinity'
HPARAMS = HParams()
Explanation: ハイパーパラメータ
モデルのトレーニングと評価のための（HParams オブジェクト内の）ハイパーパラメータを集めて説明します。
入力/出力：
input_shape：入力テンソルの形状。各画像は 28×28 ピクセルで 1 チャンネルです。
num_classes：[0-9] に対応する数字 10 個分があり、合計 10 クラスです。
モデルアーキテクチャ：
conv_filters：各畳み込みレイヤーのフィルタ数を指定する数値のリスト。
kernel_size：すべての畳み込みレイヤーで共有する 2 次元の畳み込みウィンドウのサイズ。
pool_size：各最大 Pooling レイヤーで画像をダウンスケールするための係数。
num_fc_units：完全に接続された各レイヤーの単位（すなわち幅）の数。
トレーニングと評価：
batch_size：トレーニングや評価に使用するバッチサイズ。
epochs：トレーニングのエポック数。
敵対的学習：
adv_multiplier：ラベル付けされた損失に対して相対的な、トレーニング目的内の敵対的損失の重み。
adv_step_size：敵対的摂動の大きさ。
adv_grad_norm：敵対的摂動の大きさを測るノルム。
End of explanation
datasets = tfds.load('mnist')
train_dataset = datasets['train']
test_dataset = datasets['test']
IMAGE_INPUT_NAME = 'image'
LABEL_INPUT_NAME = 'label'
Explanation: MNIST データセット
MNIST データセットには手書きの数字（ '0' から '9' まで）のグレースケール画像が含まれています。各画像は低解像度（28×28 ピクセル）で数字 1 文字を示しています。このタスクでは、画像を数字ごとに 1 つずつ、10 のカテゴリに分類します。
ここで TensorFlow Datasets から MNIST データセットを読み込みます。これはデータのダウンロードと tf.data.Dataset の構築処理をします。読み込んだデータセットには 2 つのサブセットがあります。
6 万個の例を含む train
1 万個の例を含む test
両方のサブセットに含まれる例は、以下の 2 つのキーを持つ特徴ディクショナリに格納されています。
image：0 から 255 までのピクセル値の配列。
label：0 から 9までの真の正解ラベル。
End of explanation
def normalize(features):
  features[IMAGE_INPUT_NAME] = tf.cast(
      features[IMAGE_INPUT_NAME], dtype=tf.float32) / 255.0
  return features
def convert_to_tuples(features):
  return features[IMAGE_INPUT_NAME], features[LABEL_INPUT_NAME]
def convert_to_dictionaries(image, label):
  return {IMAGE_INPUT_NAME: image, LABEL_INPUT_NAME: label}
train_dataset = train_dataset.map(normalize).shuffle(10000).batch(HPARAMS.batch_size).map(convert_to_tuples)
test_dataset = test_dataset.map(normalize).batch(HPARAMS.batch_size).map(convert_to_tuples)
Explanation: モデルを数値的に安定させるには、normalize 関数にデータセットをマッピングし、ピクセル値を [0, 1] に正規化します。トレーニングセットをシャッフルしてバッチ処理を行った後、基本モデルをトレーニングするために例を特徴タプル (image, label) に変換します。また、後で使用できるようにタプルをディクショナリに変換する関数も用意しています。
End of explanation
def build_base_model(hparams):
  Builds a model according to the architecture defined in `hparams`.
  inputs = tf.keras.Input(
      shape=hparams.input_shape, dtype=tf.float32, name=IMAGE_INPUT_NAME)
  x = inputs
  for i, num_filters in enumerate(hparams.conv_filters):
    x = tf.keras.layers.Conv2D(
        num_filters, hparams.kernel_size, activation='relu')(
            x)
    if i < len(hparams.conv_filters) - 1:
      # max pooling between convolutional layers
      x = tf.keras.layers.MaxPooling2D(hparams.pool_size)(x)
  x = tf.keras.layers.Flatten()(x)
  for num_units in hparams.num_fc_units:
    x = tf.keras.layers.Dense(num_units, activation='relu')(x)
  pred = tf.keras.layers.Dense(hparams.num_classes)(x)
  model = tf.keras.Model(inputs=inputs, outputs=pred)
  return model
base_model = build_base_model(HPARAMS)
base_model.summary()
Explanation: 基本モデル
ここでの基本モデルは、3 つの畳み込みレイヤーと 2 つの全結合レイヤー（HPARAMS の定義）で構成されるニューラルネットワークです。ここでは、Kerasu の Functional API を使用して定義しています。他の API やモデルアーキテクチャ（サブクラス化など）を自由に試してみてください。NSL フレームワークでは 3 つすべての Keras API をサポートしていることに注意してください。
End of explanation
base_model.compile(
    optimizer='adam',
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=['acc'])
base_model.fit(train_dataset, epochs=HPARAMS.epochs)
results = base_model.evaluate(test_dataset)
named_results = dict(zip(base_model.metrics_names, results))
print('\naccuracy:', named_results['acc'])
Explanation: 次に、基本モデルのトレーニングと評価を行います。
End of explanation
adv_config = nsl.configs.make_adv_reg_config(
    multiplier=HPARAMS.adv_multiplier,
    adv_step_size=HPARAMS.adv_step_size,
    adv_grad_norm=HPARAMS.adv_grad_norm
)
Explanation: 基本モデルがテストセットで 99% の精度を達成していることが分かります。下記の敵対的摂動下でのロバスト性では、モデルがどれだけロバストであるかを確認します。
敵対的正則化モデル
ここでは、NSL フレームワークを使用して、数行のコードで Keras モデルに敵対的トレーニングを組み込む方法を示します。基本モデルをラップして、トレーニング目的に敵対的正則化を含む新しい tf.Keras.Model を作成します。
まず最初に、ヘルパー関数 nsl.configs.make_adv_reg_config を使用して、関連するすべてのハイパーパラメータを含む構成オブジェクトを作成します。
End of explanation
base_adv_model = build_base_model(HPARAMS)
adv_model = nsl.keras.AdversarialRegularization(
    base_adv_model,
    label_keys=[LABEL_INPUT_NAME],
    adv_config=adv_config
)
train_set_for_adv_model = train_dataset.map(convert_to_dictionaries)
test_set_for_adv_model = test_dataset.map(convert_to_dictionaries)
Explanation: これで AdversarialRegularization を使って基本モデルをラップすることができます。ここでは、既存の基本モデル（base_model）を後で比較に使用できるように、新しい基本モデル（base_adv_model）を作成します。
返される adv_model は tf.keras.Model のオブジェクトであり、そのトレーニング目的には敵対的損失の正則化項を含みます。この損失を計算するためには、モデルは通常の正規入力（特徴 image）に加えてラベル情報（特徴 label）にアクセスする必要があります。この理由から、データセットの例は変換してタプルからディクショナリに戻します。そして label_keys パラメータを介してどの特徴がラベル情報を含んでいるかをモデルに伝えます。
End of explanation
adv_model.compile(
    optimizer='adam',
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=['acc'])
adv_model.fit(train_set_for_adv_model, epochs=HPARAMS.epochs)
results = adv_model.evaluate(test_set_for_adv_model)
named_results = dict(zip(adv_model.metrics_names, results))
print('\naccuracy:', named_results['sparse_categorical_accuracy'])
Explanation: 次に、敵対的正則化モデルをコンパイルしてトレーニングし、評価します。「損失ディクショナリに出力がありません」というような警告が出るかもしれませんが、これは adv_model が基本実装に依存せずトータルの損失を計算しているため、問題ありません。
End of explanation
reference_model = nsl.keras.AdversarialRegularization(
    base_model, label_keys=[LABEL_INPUT_NAME], adv_config=adv_config)
reference_model.compile(
    optimizer='adam',
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=['acc'])
Explanation: 敵対的正則化モデルもまた、テストセットで非常に優れた性能（99% の精度）を達成していることが分かります。
敵対的摂動下におけるロバスト性
ここでは基本モデルと敵対的正則化モデルの、敵対的摂動下におけるロバスト性を比較します。
敵対的摂動の例の生成には AdversarialRegularization.perturb_on_batch 関数を使用します。そして、これは基本モデルを基にして生成します。これを行うためには、AdversarialRegularization を使用して基本モデルをラップします。トレーニング（Model.fit）を呼び出さない限り、モデル内の学習変数に変更はなく、モデルは基本モデルのセクションのものと同じであることに注意してください。
End of explanation
models_to_eval = {
    'base': base_model,
    'adv-regularized': adv_model.base_model
}
metrics = {
    name: tf.keras.metrics.SparseCategoricalAccuracy()
    for name in models_to_eval.keys()
}
Explanation: 評価対象のモデルをディクショナリに収集し、各モデルのメトリックオブジェクトを作成します。
基本モデルと同じ（ラベル情報が不要な）入力形式を持つために adv_model.base_model を取る必要があることに注意してください。adv_model.base_model で学習する変数は、adv_model で学習する変数と同じです。
End of explanation
perturbed_images, labels, predictions = [], [], []
for batch in test_set_for_adv_model:
  perturbed_batch = reference_model.perturb_on_batch(batch)
  # Clipping makes perturbed examples have the same range as regular ones.
  perturbed_batch[IMAGE_INPUT_NAME] = tf.clip_by_value(
      perturbed_batch[IMAGE_INPUT_NAME], 0.0, 1.0)
  y_true = perturbed_batch.pop(LABEL_INPUT_NAME)
  perturbed_images.append(perturbed_batch[IMAGE_INPUT_NAME].numpy())
  labels.append(y_true.numpy())
  predictions.append({})
  for name, model in models_to_eval.items():
    y_pred = model(perturbed_batch)
    metrics[name](y_true, y_pred)
    predictions[-1][name] = tf.argmax(y_pred, axis=-1).numpy()
for name, metric in metrics.items():
  print('%s model accuracy: %f' % (name, metric.result().numpy()))
Explanation: 摂動された例を生成し、それを用いてモデルを評価するループをここに示します。摂動された画像、ラベル、予測は次のセクションで可視化するために保存しておきます。
End of explanation
batch_index = 0
batch_image = perturbed_images[batch_index]
batch_label = labels[batch_index]
batch_pred = predictions[batch_index]
batch_size = HPARAMS.batch_size
n_col = 4
n_row = (batch_size + n_col - 1) // n_col
print('accuracy in batch %d:' % batch_index)
for name, pred in batch_pred.items():
  print('%s model: %d / %d' % (name, np.sum(batch_label == pred), batch_size))
plt.figure(figsize=(15, 15))
for i, (image, y) in enumerate(zip(batch_image, batch_label)):
  y_base = batch_pred['base'][i]
  y_adv = batch_pred['adv-regularized'][i]
  plt.subplot(n_row, n_col, i+1)
  plt.title('true: %d, base: %d, adv: %d' % (y, y_base, y_adv))
  plt.imshow(tf.keras.utils.array_to_img(image), cmap='gray')
  plt.axis('off')
plt.show()
Explanation: 入力が敵対的に摂動されると、基本モデルの精度が劇的に（99% から約 50% に）低下することが分かります。一方で、敵対的正則化されたモデルの精度低下はごくわずか（99% から 95% に）です。これは、敵対的学習がモデルのロバスト性向上に有効であることを示しています。
敵対的摂動された画像の例
ここで、敵対的摂動された画像を確認してみます。摂動された画像は人間が認識可能な数字を表示していますが、基本モデルをうまく騙せることが分かります。
End of explanation
<END_TASK>