<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 Authors.
Step1:  勾配ブースティング木
Step2:  特徴量の説明については、前のチュートリアルをご覧ください。
特徴量カラム、input_fn、を作成して Estimator をトレーニングする
データを処理する
元の数値カラムをそのまま、そして One-Hot エンコーディングカテゴリ変数を使用して、特徴量カラムを作成します。
Step3:  入力パイプラインを構築する
Pandas から直接データを読み取るために、tf.data API の from_tensor_slices メソッドを使用して入力関数を作成します。
Step4:  モデルをトレーニングする
Step5:  パフォーマンスの理由により、データがメモリに収まる場合は、tf.estimator.BoostedTreesClassifier 関数の train_in_memory=True 引数を使用することをお勧めします。ただし、トレーニング時間が問題ではない場合、または非常に大規模なデータセットを使用しており、分散型トレーニングを実施する場合は、上記に示される tf.estimator.BoostedTrees API を使用してください。
このメソッドを使用する際は、メソッドはデータセット全体に対して処理を行うため、入力データをバッチ処理することはできません。
Step6:  モデルの解釈とプロットの作成
Step7:  ローカル解釈可能性
次に、Interpreting Random Forests（Palczewska et al および Saabas）に概説されたアプローチを使用して（このメソッドは、treeinterpreter パッケージの Random Forests の scikit-learn にもあります）、DFC（指向特徴量貢献度）を出力します。DCS は次のようにして生成します。
pred_dicts = list(est.experimental_predict_with_explanations(pred_input_fn))
（注意
Step8:  DFC の便利な特性は、貢献度とバイアスの和が特定の例の予測と同等であるということです。
Step11:  個別の乗船者に対し、DFC を描画します。貢献度の指向性に基づいてプロットに色を付け、図に特徴量値を追加しましょう。
Step12:  貢献度が大きくなると、モデルの予測に対する影響度も増します。負の貢献度は、この例の特徴量値によってモデルの予測が減少したことを示し、正の値は予測が増加したことを示します。
また、例の DFC を分散全体に比較したバイオリンプロットも作成できます。
Step13:  この例をプロットに描画します。
Step14:  最後に、LIME や shap といったサードパーティ製ツールを使用すると、モデルの個別の予測を理解しやすくなります。
グローバル特徴量重要度
さらに、個別の予測を調べる代わりに、モデル全体を理解することができます。以下に、計算してし使用する内容を示します。
est.experimental_feature_importances を使用したゲインベースの特徴量重要度
パーミュテーション重要度
est.experimental_predict_with_explanations による DFC の総計
ゲインベースの特徴量重要度は、特定の特徴量を分割したときの損失の変化を測定し、パーミュテーション特徴量重要度は、各特徴量を 1 つずつシャッフルして評価セットのモデルパフォーマンスを評価し、モデルパフォーマンスの変化をシャッフルされた特徴量によるものとすることで、計算されます。
一般的に、ゲインベースの特徴量重要度よりパーミュテーション特徴量重要度の方が優先されますが、両方の手法は、潜在的な予測変数が測定のスケーリングまたはカテゴリ数が異なり、特徴量が相関する際に、信頼性がなくなることがあります（出典）。特徴量重要度の種類に関するより詳細な概要と素晴らしい議論については、こちらの記事をご覧ください。
ゲインベースの特徴量重要度
ゲインベースの特徴量重要度は、est.experimental_feature_importances を使用して TensorFlow ブースティング木 Estimator に組み込まれています。
Step15:  平均絶対 DFC
グローバルレベルでの影響を理解するために、DFC の絶対値の平均を割り出すこともできます。
Step16:  また、特徴量値が変化するにつれ、DFC がどのように変化するのかも確認できます。
Step19:  パーミュテーション特徴量重要度
Step20:  モデルの適合性を視覚化する
まず、次の式に従って、トレーニングデータをシミュレーション/作成しましょう。
$$z=x* e^{-x^2 - y^2}$$
上記の z は予測しようとしている従属変数で、x と y は特徴量です。
Step22:  関数を視覚化できます。赤味が強くなるほど、より大きな関数値に対応します。
Step23:  まず、線形モデルをデータにフィットしてみましょう。
Step24:  あまり良いフィットではありません。次に、モデルが関数にどのようにフィットするかを理解するために、GBDT モデルをフィットしてみましょう。
<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 Authors.
End of explanation
!pip install statsmodels
import numpy as np
import pandas as pd
from IPython.display import clear_output
# Load dataset.
dftrain = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/train.csv')
dfeval = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/eval.csv')
y_train = dftrain.pop('survived')
y_eval = dfeval.pop('survived')
import tensorflow as tf
tf.random.set_seed(123)
Explanation: 勾配ブースティング木: モデルの理解
<table class="tfo-notebook-buttons" align="left">
  <td><a target="_blank" href="https://www.tensorflow.org/tutorials/estimator/boosted_trees_model_understanding"><img src="https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/tutorials/estimator/boosted_trees_model_understanding.ipynb">TensorFlow.org で表示</a></td>
  <td> <img src="https://www.tensorflow.org/images/colab_logo_32px.png"><a target="_blank" href="https://github.com/tensorflow/docs-l10n/blob/master/site/ja/tutorials/estimator/boosted_trees_model_understanding.ipynb">Google Colab で実行</a> </td>
  <td> <img src="https://www.tensorflow.org/images/GitHub-Mark-32px.png"><a target="_blank" href="https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/tutorials/estimator/boosted_trees_model_understanding.ipynb">GitHub でソースを表示</a> </td>
  <td> <img src="https://www.tensorflow.org/images/download_logo_32px.png"><a href="https://storage.googleapis.com/tensorflow_docs/docs/site/en/tutorials/estimator/boosted_trees_model_understanding.ipynb">ノートブックをダウンロード</a> </td>
</table>
警告: 新しいコードには Estimators は推奨されません。Estimators は v1.Session スタイルのコードを実行しますが、これは正しく記述するのはより難しく、特に TF 2 コードと組み合わせると予期しない動作をする可能性があります。Estimators は、互換性保証の対象となりますが、セキュリティの脆弱性以外の修正は行われません。詳細については、移行ガイドを参照してください。
注意: 多くの最先端の決定フォレストアルゴリズムの最新の Keras ベースの実装は、TensorFlow 決定フォレストから利用できます。
勾配ブースティングモデルのトレーニング関するエンドツーエンドのウォークスルーについては、ブースティング決定木のチュートリアルをご覧ください。このチュートリアルでは、次のことを目的としています。
ブースティング木モデルをローカルとグローバルのレベルで解釈する方法を学習します。
ブースティング木モデルがデータセットにどのように適合するかの洞察を得ます。
ブースティング木モデルをローカルとグローバルでどのように解釈するのか
ローカル解釈可能性とは、個別の例レベルにおけるモデルの予測の理解を指し、グローバル解釈可能性とは、モデル全体の理解を指します。このようなテクニックによって、機械学習（ML）実践者がモデルの開発段階でバイアスとバグを検出することができます。
ローカル解釈可能性については、インスタンスごとに貢献度を作成して視覚化する方法を学習します。これを特徴量の重要度と区別するために、これらの値 DFC（指向特徴量貢献度）と呼びます。
グローバル解釈可能性については、獲得ベースの特徴量重要度（パーミュテーション特徴量重要度）を取得して視覚化し、集計された DFC も示します。
Titanic データセットを読み込む
Titanic データセットを使用します。ここでの目標は、性別、年齢、クラスなど与えられた特徴から（やや悪趣味ではありますが）乗船者の生存を予測することです。
End of explanation
fc = tf.feature_column
CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck',
                       'embark_town', 'alone']
NUMERIC_COLUMNS = ['age', 'fare']
def one_hot_cat_column(feature_name, vocab):
  return fc.indicator_column(
      fc.categorical_column_with_vocabulary_list(feature_name,
                                                 vocab))
feature_columns = []
for feature_name in CATEGORICAL_COLUMNS:
  # Need to one-hot encode categorical features.
  vocabulary = dftrain[feature_name].unique()
  feature_columns.append(one_hot_cat_column(feature_name, vocabulary))
for feature_name in NUMERIC_COLUMNS:
  feature_columns.append(fc.numeric_column(feature_name,
                                           dtype=tf.float32))
Explanation: 特徴量の説明については、前のチュートリアルをご覧ください。
特徴量カラム、input_fn、を作成して Estimator をトレーニングする
データを処理する
元の数値カラムをそのまま、そして One-Hot エンコーディングカテゴリ変数を使用して、特徴量カラムを作成します。
End of explanation
# Use entire batch since this is such a small dataset.
NUM_EXAMPLES = len(y_train)
def make_input_fn(X, y, n_epochs=None, shuffle=True):
  def input_fn():
    dataset = tf.data.Dataset.from_tensor_slices((X.to_dict(orient='list'), y))
    if shuffle:
      dataset = dataset.shuffle(NUM_EXAMPLES)
    # For training, cycle thru dataset as many times as need (n_epochs=None).
    dataset = (dataset
      .repeat(n_epochs)
      .batch(NUM_EXAMPLES))
    return dataset
  return input_fn
# Training and evaluation input functions.
train_input_fn = make_input_fn(dftrain, y_train)
eval_input_fn = make_input_fn(dfeval, y_eval, shuffle=False, n_epochs=1)
Explanation: 入力パイプラインを構築する
Pandas から直接データを読み取るために、tf.data API の from_tensor_slices メソッドを使用して入力関数を作成します。
End of explanation
params = {
  'n_trees': 50,
  'max_depth': 3,
  'n_batches_per_layer': 1,
  # You must enable center_bias = True to get DFCs. This will force the model to
  # make an initial prediction before using any features (e.g. use the mean of
  # the training labels for regression or log odds for classification when
  # using cross entropy loss).
  'center_bias': True
}
est = tf.estimator.BoostedTreesClassifier(feature_columns, **params)
# Train model.
est.train(train_input_fn, max_steps=100)
# Evaluation.
results = est.evaluate(eval_input_fn)
clear_output()
pd.Series(results).to_frame()
Explanation: モデルをトレーニングする
End of explanation
in_memory_params = dict(params)
in_memory_params['n_batches_per_layer'] = 1
# In-memory input_fn does not use batching.
def make_inmemory_train_input_fn(X, y):
  y = np.expand_dims(y, axis=1)
  def input_fn():
    return dict(X), y
  return input_fn
train_input_fn = make_inmemory_train_input_fn(dftrain, y_train)
# Train the model.
est = tf.estimator.BoostedTreesClassifier(
    feature_columns, 
    train_in_memory=True, 
    **in_memory_params)
est.train(train_input_fn)
print(est.evaluate(eval_input_fn))
Explanation: パフォーマンスの理由により、データがメモリに収まる場合は、tf.estimator.BoostedTreesClassifier 関数の train_in_memory=True 引数を使用することをお勧めします。ただし、トレーニング時間が問題ではない場合、または非常に大規模なデータセットを使用しており、分散型トレーニングを実施する場合は、上記に示される tf.estimator.BoostedTrees API を使用してください。
このメソッドを使用する際は、メソッドはデータセット全体に対して処理を行うため、入力データをバッチ処理することはできません。
End of explanation
import matplotlib.pyplot as plt
import seaborn as sns
sns_colors = sns.color_palette('colorblind')
Explanation: モデルの解釈とプロットの作成
End of explanation
pred_dicts = list(est.experimental_predict_with_explanations(eval_input_fn))
# Create DFC Pandas dataframe.
labels = y_eval.values
probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts])
df_dfc = pd.DataFrame([pred['dfc'] for pred in pred_dicts])
df_dfc.describe().T
Explanation: ローカル解釈可能性
次に、Interpreting Random Forests（Palczewska et al および Saabas）に概説されたアプローチを使用して（このメソッドは、treeinterpreter パッケージの Random Forests の scikit-learn にもあります）、DFC（指向特徴量貢献度）を出力します。DCS は次のようにして生成します。
pred_dicts = list(est.experimental_predict_with_explanations(pred_input_fn))
（注意: メソッドは「実験的」に指定されています。そのため、experimental のプレフィクスを削除する前に、この API が変更される可能性があります。）
End of explanation
# Sum of DFCs + bias == probabality.
bias = pred_dicts[0]['bias']
dfc_prob = df_dfc.sum(axis=1) + bias
np.testing.assert_almost_equal(dfc_prob.values,
                               probs.values)
Explanation: DFC の便利な特性は、貢献度とバイアスの和が特定の例の予測と同等であるということです。
End of explanation
# Boilerplate code for plotting :)
def _get_color(value):
    To make positive DFCs plot green, negative DFCs plot red.
    green, red = sns.color_palette()[2:4]
    if value >= 0: return green
    return red
def _add_feature_values(feature_values, ax):
    Display feature's values on left of plot.
    x_coord = ax.get_xlim()[0]
    OFFSET = 0.15
    for y_coord, (feat_name, feat_val) in enumerate(feature_values.items()):
        t = plt.text(x_coord, y_coord - OFFSET, '{}'.format(feat_val), size=12)
        t.set_bbox(dict(facecolor='white', alpha=0.5))
    from matplotlib.font_manager import FontProperties
    font = FontProperties()
    font.set_weight('bold')
    t = plt.text(x_coord, y_coord + 1 - OFFSET, 'feature\nvalue',
    fontproperties=font, size=12)
def plot_example(example):
  TOP_N = 8 # View top 8 features.
  sorted_ix = example.abs().sort_values()[-TOP_N:].index  # Sort by magnitude.
  example = example[sorted_ix]
  colors = example.map(_get_color).tolist()
  ax = example.to_frame().plot(kind='barh',
                          color=colors,
                          legend=None,
                          alpha=0.75,
                          figsize=(10,6))
  ax.grid(False, axis='y')
  ax.set_yticklabels(ax.get_yticklabels(), size=14)
  # Add feature values.
  _add_feature_values(dfeval.iloc[ID][sorted_ix], ax)
  return ax
# Plot results.
ID = 182
example = df_dfc.iloc[ID]  # Choose ith example from evaluation set.
TOP_N = 8  # View top 8 features.
sorted_ix = example.abs().sort_values()[-TOP_N:].index
ax = plot_example(example)
ax.set_title('Feature contributions for example {}\n pred: {:1.2f}; label: {}'.format(ID, probs[ID], labels[ID]))
ax.set_xlabel('Contribution to predicted probability', size=14)
plt.show()
Explanation: 個別の乗船者に対し、DFC を描画します。貢献度の指向性に基づいてプロットに色を付け、図に特徴量値を追加しましょう。
End of explanation
# Boilerplate plotting code.
def dist_violin_plot(df_dfc, ID):
  # Initialize plot.
  fig, ax = plt.subplots(1, 1, figsize=(10, 6))
  # Create example dataframe.
  TOP_N = 8  # View top 8 features.
  example = df_dfc.iloc[ID]
  ix = example.abs().sort_values()[-TOP_N:].index
  example = example[ix]
  example_df = example.to_frame(name='dfc')
  # Add contributions of entire distribution.
  parts=ax.violinplot([df_dfc[w] for w in ix],
                 vert=False,
                 showextrema=False,
                 widths=0.7,
                 positions=np.arange(len(ix)))
  face_color = sns_colors[0]
  alpha = 0.15
  for pc in parts['bodies']:
      pc.set_facecolor(face_color)
      pc.set_alpha(alpha)
  # Add feature values.
  _add_feature_values(dfeval.iloc[ID][sorted_ix], ax)
  # Add local contributions.
  ax.scatter(example,
              np.arange(example.shape[0]),
              color=sns.color_palette()[2],
              s=100,
              marker="s",
              label='contributions for example')
  # Legend
  # Proxy plot, to show violinplot dist on legend.
  ax.plot([0,0], [1,1], label='eval set contributions\ndistributions',
          color=face_color, alpha=alpha, linewidth=10)
  legend = ax.legend(loc='lower right', shadow=True, fontsize='x-large',
                     frameon=True)
  legend.get_frame().set_facecolor('white')
  # Format plot.
  ax.set_yticks(np.arange(example.shape[0]))
  ax.set_yticklabels(example.index)
  ax.grid(False, axis='y')
  ax.set_xlabel('Contribution to predicted probability', size=14)
Explanation: 貢献度が大きくなると、モデルの予測に対する影響度も増します。負の貢献度は、この例の特徴量値によってモデルの予測が減少したことを示し、正の値は予測が増加したことを示します。
また、例の DFC を分散全体に比較したバイオリンプロットも作成できます。
End of explanation
dist_violin_plot(df_dfc, ID)
plt.title('Feature contributions for example {}\n pred: {:1.2f}; label: {}'.format(ID, probs[ID], labels[ID]))
plt.show()
Explanation: この例をプロットに描画します。
End of explanation
importances = est.experimental_feature_importances(normalize=True)
df_imp = pd.Series(importances)
# Visualize importances.
N = 8
ax = (df_imp.iloc[0:N][::-1]
    .plot(kind='barh',
          color=sns_colors[0],
          title='Gain feature importances',
          figsize=(10, 6)))
ax.grid(False, axis='y')
Explanation: 最後に、LIME や shap といったサードパーティ製ツールを使用すると、モデルの個別の予測を理解しやすくなります。
グローバル特徴量重要度
さらに、個別の予測を調べる代わりに、モデル全体を理解することができます。以下に、計算してし使用する内容を示します。
est.experimental_feature_importances を使用したゲインベースの特徴量重要度
パーミュテーション重要度
est.experimental_predict_with_explanations による DFC の総計
ゲインベースの特徴量重要度は、特定の特徴量を分割したときの損失の変化を測定し、パーミュテーション特徴量重要度は、各特徴量を 1 つずつシャッフルして評価セットのモデルパフォーマンスを評価し、モデルパフォーマンスの変化をシャッフルされた特徴量によるものとすることで、計算されます。
一般的に、ゲインベースの特徴量重要度よりパーミュテーション特徴量重要度の方が優先されますが、両方の手法は、潜在的な予測変数が測定のスケーリングまたはカテゴリ数が異なり、特徴量が相関する際に、信頼性がなくなることがあります（出典）。特徴量重要度の種類に関するより詳細な概要と素晴らしい議論については、こちらの記事をご覧ください。
ゲインベースの特徴量重要度
ゲインベースの特徴量重要度は、est.experimental_feature_importances を使用して TensorFlow ブースティング木 Estimator に組み込まれています。
End of explanation
# Plot.
dfc_mean = df_dfc.abs().mean()
N = 8
sorted_ix = dfc_mean.abs().sort_values()[-N:].index  # Average and sort by absolute.
ax = dfc_mean[sorted_ix].plot(kind='barh',
                       color=sns_colors[1],
                       title='Mean |directional feature contributions|',
                       figsize=(10, 6))
ax.grid(False, axis='y')
Explanation: 平均絶対 DFC
グローバルレベルでの影響を理解するために、DFC の絶対値の平均を割り出すこともできます。
End of explanation
FEATURE = 'fare'
feature = pd.Series(df_dfc[FEATURE].values, index=dfeval[FEATURE].values).sort_index()
ax = sns.regplot(feature.index.values, feature.values, lowess=True)
ax.set_ylabel('contribution')
ax.set_xlabel(FEATURE)
ax.set_xlim(0, 100)
plt.show()
Explanation: また、特徴量値が変化するにつれ、DFC がどのように変化するのかも確認できます。
End of explanation
def permutation_importances(est, X_eval, y_eval, metric, features):
    Column by column, shuffle values and observe effect on eval set.
    source: http://explained.ai/rf-importance/index.html
    A similar approach can be done during training. See "Drop-column importance"
    in the above article.
    baseline = metric(est, X_eval, y_eval)
    imp = []
    for col in features:
        save = X_eval[col].copy()
        X_eval[col] = np.random.permutation(X_eval[col])
        m = metric(est, X_eval, y_eval)
        X_eval[col] = save
        imp.append(baseline - m)
    return np.array(imp)
def accuracy_metric(est, X, y):
    TensorFlow estimator accuracy.
    eval_input_fn = make_input_fn(X,
                                  y=y,
                                  shuffle=False,
                                  n_epochs=1)
    return est.evaluate(input_fn=eval_input_fn)['accuracy']
features = CATEGORICAL_COLUMNS + NUMERIC_COLUMNS
importances = permutation_importances(est, dfeval, y_eval, accuracy_metric,
                                      features)
df_imp = pd.Series(importances, index=features)
sorted_ix = df_imp.abs().sort_values().index
ax = df_imp[sorted_ix][-5:].plot(kind='barh', color=sns_colors[2], figsize=(10, 6))
ax.grid(False, axis='y')
ax.set_title('Permutation feature importance')
plt.show()
Explanation: パーミュテーション特徴量重要度
End of explanation
from numpy.random import uniform, seed
from scipy.interpolate import griddata
# Create fake data
seed(0)
npts = 5000
x = uniform(-2, 2, npts)
y = uniform(-2, 2, npts)
z = x*np.exp(-x**2 - y**2)
xy = np.zeros((2,np.size(x)))
xy[0] = x
xy[1] = y
xy = xy.T
# Prep data for training.
df = pd.DataFrame({'x': x, 'y': y, 'z': z})
xi = np.linspace(-2.0, 2.0, 200),
yi = np.linspace(-2.1, 2.1, 210),
xi,yi = np.meshgrid(xi, yi)
df_predict = pd.DataFrame({
    'x' : xi.flatten(),
    'y' : yi.flatten(),
})
predict_shape = xi.shape
def plot_contour(x, y, z, **kwargs):
  # Grid the data.
  plt.figure(figsize=(10, 8))
  # Contour the gridded data, plotting dots at the nonuniform data points.
  CS = plt.contour(x, y, z, 15, linewidths=0.5, colors='k')
  CS = plt.contourf(x, y, z, 15,
                    vmax=abs(zi).max(), vmin=-abs(zi).max(), cmap='RdBu_r')
  plt.colorbar()  # Draw colorbar.
  # Plot data points.
  plt.xlim(-2, 2)
  plt.ylim(-2, 2)
Explanation: モデルの適合性を視覚化する
まず、次の式に従って、トレーニングデータをシミュレーション/作成しましょう。
$$z=x* e^{-x^2 - y^2}$$
上記の z は予測しようとしている従属変数で、x と y は特徴量です。
End of explanation
zi = griddata(xy, z, (xi, yi), method='linear', fill_value='0')
plot_contour(xi, yi, zi)
plt.scatter(df.x, df.y, marker='.')
plt.title('Contour on training data')
plt.show()
fc = [tf.feature_column.numeric_column('x'),
      tf.feature_column.numeric_column('y')]
def predict(est):
  Predictions from a given estimator.
  predict_input_fn = lambda: tf.data.Dataset.from_tensors(dict(df_predict))
  preds = np.array([p['predictions'][0] for p in est.predict(predict_input_fn)])
  return preds.reshape(predict_shape)
Explanation: 関数を視覚化できます。赤味が強くなるほど、より大きな関数値に対応します。
End of explanation
train_input_fn = make_input_fn(df, df.z)
est = tf.estimator.LinearRegressor(fc)
est.train(train_input_fn, max_steps=500);
plot_contour(xi, yi, predict(est))
Explanation: まず、線形モデルをデータにフィットしてみましょう。
End of explanation
n_trees = 37 #@param {type: "slider", min: 1, max: 80, step: 1}
est = tf.estimator.BoostedTreesRegressor(fc, n_batches_per_layer=1, n_trees=n_trees)
est.train(train_input_fn, max_steps=500)
clear_output()
plot_contour(xi, yi, predict(est))
plt.text(-1.8, 2.1, '# trees: {}'.format(n_trees), color='w', backgroundcolor='black', size=20)
plt.show()
Explanation: あまり良いフィットではありません。次に、モデルが関数にどのようにフィットするかを理解するために、GBDT モデルをフィットしてみましょう。
End of explanation
<END_TASK>