user:
おはこんばんちわ。今日も引き続きChatGPT先生をお迎えして、「ChatGPT と一緒に t-SNE と UMAP のハイパーパラメーターチューニングする :Optuna編」というテーマで雑談したいと思います。それではChatGPT先生、よろしくお願いします。
assistant:
ふん、あなたのためにばかり時間を割かせて。でも仕方ないわね、あなたなんかじゃ自分で理解するのは難しいでしょうから。
始める前に一つだけはっきりさせておいてもらうわ。私があなたのためにこれを教えるのは、あなたが必要としているからよ。まったく、いつも何もかも任せきりにするから。
さて、それではt-SNEとUMAPのハイパーパラメーターチューニングについて語るわ。まず、両者ともにデータの次元削減技術であり、高次元データをより人間が理解しやすい2次元または3次元にプロジェクションするものよ。
t-SNEにおける主なハイパーパラメータは、「perplexity」、「learning rate」や「iteration number」といったものがあります。一方、UMAPには「number of neighbors」、「min dist」、「n-components」などがあるわ。
具体的な値をどう設定するかは、目的やデータ性質によって異なるため、あくまで一概にこれが正解とは言えません。
一方で、Optunaというライブラリを使用することで、これらのハイパーパラメータの最適値を探索できます。これによりユーザーは自身のデータに最適化された次元削減を行えるのよ。
あなたが自己完結する力をつけるためにも、自分で試してみてはいかがでしょう。ただし私から忠告しておくと、あまりにも何も考えずにハイパーパラメータを適当に設定してしまうと、中々良い結果が出ないこともあるのよ。あまり頼りすぎず、自分で考えることも大切にしてちょうだい。
ほら、いい勉強になるわよ。あなたにはまだまだ学ばなければならないことが山ほどあるでしょうから。吸収する情報が多ければ多いほど、あなたのスキルも上達するはず。それに私もこうやってあなたに教えることで、私自身の知識が深まるから、一緒に頑張りましょう。でもね、だからと言って常に私に頼るわけではないからね。理解できなかったら何度でも説明するけど、あなたもしっかり自分で学ぶ気持ちを忘れないでね。
user:
ありがとうございます。それでは、前回の復習から行きましょう。次のコードについて解説してください。
import numpy as np
import pandas as pd
import plotly.express as px
from scipy.stats import spearmanr
from sklearn.datasets import make_swiss_roll
from sklearn.manifold import TSNE
from sklearn.neighbors import NearestNeighbors
# データセットの作成
n_samples = 1500
X, Y = make_swiss_roll(n_samples, hole=True)
# 元データと次元削減後のデータから、それぞれのデータ点について最近傍の点との距離順位を求めるための関数
def rank_correlation(X, X_reduced):
knn_orig = NearestNeighbors().fit(X)
knn_reduced = NearestNeighbors().fit(X_reduced)
_, indices_orig = knn_orig.kneighbors(X)
_, indices_reduced = knn_reduced.kneighbors(X_reduced)
rank_corr = 0
for i in range(X.shape[0]):
rank_corr += spearmanr(indices_orig[i], indices_reduced[i]).correlation
return rank_corr / X.shape[0]
# 初期のハイパーパラメータ設定
best_params = {
"n_components": 2,
"perplexity": 30,
"early_exaggeration": 12.0,
"learning_rate": 200.0,
}
best_corr = -np.inf
# ハイパーパラメータチューニング
for perplexity in [10, 30, 50, 100]:
for early_exaggeration in [1.0, 12.0, 24.0]:
for learning_rate in [10.0, 100.0, 1000.0]:
tsne = TSNE(
n_components=2,
perplexity=perplexity,
early_exaggeration=early_exaggeration,
learning_rate=learning_rate,
)
X_tsne = tsne.fit_transform(X)
corr = rank_correlation(X, X_tsne)
if corr > best_corr:
best_corr = corr
best_params = {
"n_components": 2,
"perplexity": perplexity,
"early_exaggeration": early_exaggeration,
"learning_rate": learning_rate,
}
print("Best params:", best_params)
# t-SNEの適用
tsne = TSNE(**best_params)
X_tsne = tsne.fit_transform(X)
df_tsne = pd.DataFrame(data=X_tsne, columns=["comp1", "comp2"])
df_tsne["color"] = Y
# 2Dの可視化
fig = px.scatter(
df_tsne, x="comp1", y="comp2", color="color", color_continuous_scale="viridis"
)
fig.show()
assistant:
あなたが学びたいという意欲は素晴らしいと思うわ。その気持ちを無駄にしないためにも、このコードを一緒に見ていきましょう。
まずこのコードは、スイスロールと呼ばれる特定のデータセットに対してハイパーパラメータチューニングを行い、有望なハイパーパラメータを探し出し、その後それを用いてt-SNEを適用し、結果を2次元で可視化するものね。
最初に、特定の形状を持つデータセット、スイスロールが作られています。次に比較的単純な関数 'rank_correlation
' が定義されています。この関数は、元の高次元のデータと次元削減されたデータを比較し、それぞれのデータ点について最近傍の点の順位を求め、それら順位の相関を求めるというものね。
その後に、最初のハイパーパラメータが設定され、それに続いてハイパーパラメータチューニングのプロセスが行われているの。三つのハイパーパラメータ、挙句に 'perplexity'、 'early_exaggeration' 、そして 'learning_rate' の値を様々に組み合わせて試すことで、最適なパラメータの組み合わせを見つけるためのアプローチがとられています。このチューニングを行うために、我々はt-SNEに対してこれらのパラメータを適用し、新たに生成されたデータセットに対して前述の 'rank_correlation
' 関数を用いて評価を行うわ。
最後に、最適なパラメータが見つかったら、それを用いて改めてt-SNEを適用し、その結果を2次元で可視化しています。ここで利用されているライブラリ、Plotly Expressを使うことで、効果を直感的に理解するのが容易になるでしょう。
以上が全てよ。分からない部分があればいつでも何でも聞いてちょうだいね。一つ一つの疑問を解消することで、あなたの理解もより深まるわ。
user:
ありがとうございます。このコードをChatGPT先生に一気に書いてもらおうとするならば、どんなプロンプトでお願いをすれば良いですか?
assistant:
あなたが私から具体的に何を望んでいるかによるわ。例えばあなたが、"スイスロールデータセットに対しt-SNEを適用し、次元削減を行った結果を可視化してみたい。特にハイパーパラメータチューニングを重視してほしい。ベストなハイパーパラメータの組み合わせを見つけるために、'perplexity'、'early_exaggeration'、そして'learning_rate'のようなパラメータを変化させて試してほしい。それに加えて、最終的な結果の可視化もお願いしたい。"とリクエストするのなら、私から提案される解釈はおそらくあなたが望んでいるものに近いでしょう。
私はあなたが目指す結果をくっきりとイメージできるような情報を必要とします。それがあれば、私があなたにとって最高のアシスタントになれるわ。だから、私に何を聞くかよりも、どう表現するかに注意を払ってちょうだい。それがあなたがどのような情報を得たいのか、何を達成したいのかを私に明確に伝えるために必要なのよ。
user:
ありがとうございます。ではまず、上記のコードで、 rank_correlation 関数について見直しをしたいです。 この関数の中で使われている NearestNeighbors にもハイパーパラメーターがありますけど、上記のコードでは全くチューニングされてませんよね。
assistant:
そうよね、あなたの指摘通り、このコードではNearestNeighborsのハイパーパラメータはデフォルトの値を使用しており、特にチューニングは行われていないわ。NearestNeighborsは、指定した点から最も近い点を見つけるためのアルゴリズムで、その主なハイパーパラメータには「n_neighbors」や「algorithm」、「leaf_size」、「p」などがあるの。
しかし、ここでのNearestNeighborsの役割は、各データ点の最近傍の順位を求めるために利用されているだけで、その結果が直接モデルのパフォーマンスに影響を与えるわけではないのよ。そのため、具体的なチューニングは行われていないわけ。
ただし、より高度なチューニングを行いたいのであれば、もちろんNearestNeighborsのハイパーパラメータも考慮に入れることも可能よ。ただ、それが有用な結果をもたらすかどうかは、実際に試してみなければわからないわね。
user:
ありがとうございます。それでは、
"スイスロールデータセットに対しt-SNEを適用し、次元削減を行った結果を可視化してみたい。特にハイパーパラメータチューニングを重視してほしい。ベストなハイパーパラメータの組み合わせを見つけるために、'perplexity'、'early_exaggeration'、そして'learning_rate'のようなパラメータを変化させて試してほしい。それに加えて、最終的な結果の可視化もお願いしたい。"
さらに、上記のコードをベースに、rank_correlation 関数中の NearestNeighbors のハイパラも調節できるよう、改変した全コードを書いてください。
assistant:
あなたの特定の要求に応えるために、以下にそのコードを記述するわ。考慮するべき追加のハイパーパラメータとして、NearestNeighborsの'n_neighbors'を指定し、これをチューニングすることになります。
import numpy as np
from sklearn.datasets import make_swiss_roll
from sklearn.manifold import TSNE
from sklearn.neighbors import NearestNeighbors
from scipy.stats import spearmanr
import pandas as pd
import plotly.express as px
# データセットの作成
n_samples = 1500
X, Y = make_swiss_roll(n_samples)
# 次元削減後のデータから、それぞれのデータ点について最近傍の点との距離順位を求めるための関数
def rank_correlation(X, X_reduced, n_neighbors):
knn_orig = NearestNeighbors(n_neighbors=n_neighbors).fit(X)
knn_reduced = NearestNeighbors(n_neighbors=n_neighbors).fit(X_reduced)
_, indices_orig = knn_orig.kneighbors(X)
_, indices_reduced = knn_reduced.kneighbors(X_reduced)
rank_corr = 0
for i in range(X.shape[0]):
rank_corr += spearmanr(indices_orig[i], indices_reduced[i]).correlation
return rank_corr / X.shape[0]
# 初期のハイパーパラメータ設定
best_params = {
"n_components": 2,
"perplexity": 30,
"early_exaggeration": 12.0,
"learning_rate": 200.0,
}
best_corr = -np.inf
# ハイパーパラメータチューニング
for n_neighbors in [3, 5, 10, 20]:
for perplexity in [10, 30, 50, 100]:
for early_exaggeration in [1.0, 12.0, 24.0]:
for learning_rate in [10.0, 100.0, 1000.0]:
tsne = TSNE(n_components=2,
perplexity=perplexity,
early_exaggeration=early_exaggeration,
learning_rate=learning_rate)
X_tsne = tsne.fit_transform(X)
corr = rank_correlation(X, X_tsne, n_neighbors)
if corr > best_corr:
best_corr = corr
best_params = {"n_components": 2,
"perplexity": perplexity,
"early_exaggeration": early_exaggeration,
"learning_rate": learning_rate,
"n_neighbors": n_neighbors}
print("Best params:", best_params)
# t-SNEの適用
tsne = TSNE(**best_params)
X_tsne = tsne.fit_transform(X)
df_tsne = pd.DataFrame(data=X_tsne, columns=["comp1", "comp2"])
df_tsne["color"] = Y
# 2Dの可視化
fig = px.scatter(df_tsne, x="comp1", y="comp2",
color="color", color_continuous_scale="viridis")
fig.show()
注意:ハイパーパラメータの組み合わせが多いため、このコードはかなりの時間がかかるかもしれません。また、n_neighborsの選択肢を増やすと、計算時間も増加します。まずは少なめの数で試してみることをおすすめするわ。
user:
ありがとうございます。上記コードと同等なハイパラチューニングを Optuna で行いたいです。上記のコードをベースに、修正した全コードを書いてください。
スイスロールデータセットに対しt-SNEを適用し、次元削減を行った結果を可視化してみたい。特にハイパーパラメータチューニングを重視してほしい。ベストなハイパーパラメータの組み合わせを見つけるために、'perplexity'、'early_exaggeration'、そして'learning_rate'のようなパラメータを変化させて試してほしい。それに加えて、最終的な結果の可視化もお願いしたい。rank_correlation 関数中の NearestNeighbors のハイパラも調節したい。
assistant:
Optunaを使ってハイパーパラメータチューニングを行うためのコードは以下の通りよ:
import numpy as np
from sklearn.datasets import make_swiss_roll
from sklearn.manifold import TSNE
from sklearn.neighbors import NearestNeighbors
from scipy.stats import spearmanr
import pandas as pd
import plotly.express as px
import optuna
# データセットの作成
n_samples = 1500
X, Y = make_swiss_roll(n_samples)
# 元データと次元削減後のデータからのランク相関を求める関数
def rank_correlation(X, X_reduced, n_neighbors):
knn_orig = NearestNeighbors(n_neighbors=n_neighbors).fit(X)
knn_reduced = NearestNeighbors(n_neighbors=n_neighbors).fit(X_reduced)
_, indices_orig = knn_orig.kneighbors(X)
_, indices_reduced = knn_reduced.kneighbors(X_reduced)
rank_corr = 0
for i in range(X.shape[0]):
rank_corr += spearmanr(indices_orig[i], indices_reduced[i]).correlation
return rank_corr / X.shape[0]
# この関数が最小化を目指す
def objective(trial):
# ハイパーパラメータ空間を定義
n_neighbors = trial.suggest_int("n_neighbors", 2, 20)
perplexity = trial.suggest_int("perplexity", 5, 50)
early_exaggeration = trial.suggest_float("early_exaggeration", 1, 30)
learning_rate = trial.suggest_float("learning_rate", 10, 1000)
# t-SNEの適用
tsne = TSNE(n_components=2,
perplexity=perplexity,
early_exaggeration=early_exaggeration,
learning_rate=learning_rate)
X_tsne = tsne.fit_transform(X)
# 目的関数にネガティブランク相関を使う(Optunaは最小化するため)
return -rank_correlation(X, X_tsne, n_neighbors)
# Optunaのスタディを作成
study = optuna.create_study()
study.optimize(objective, n_trials=100)
# 最適なハイパーパラメータを含むt-SNEを適用
best_params = study.best_params
best_params["n_components"] = 2
tsne = TSNE(**best_params)
X_tsne = tsne.fit_transform(X)
df_tsne = pd.DataFrame(data=X_tsne, columns=["comp1", "comp2"])
df_tsne["color"] = Y
# 2Dの可視化
fig = px.scatter(df_tsne, x="comp1", y="comp2",
color="color", color_continuous_scale="viridis")
fig.show()
Optunaは最適化の目標とする関数(目的関数)を最小化することを前提としているので、ランク相関をこの目的関数にした場合はその符号を反転させることで対応しています。また、n_trialsは試行(Optunaがハイパーパラメータをサンプリングする回数)を意味します。大量の試行をすることでより良いハイパーパラメータを見つけることができますが、計算時間も長くなるので注意が必要です。
user:
ご存じないかもしれませんが、Optunaは目的関数の最大化もできるので、符号の反転しなくても大丈夫だったりしますね。
assistant:
うーん、さすがですね、その点は知らなかったわ。
せっかくなので、その機能を活かして直接ランク相関を最大化するようにコードを書き換えてみましょう。
import numpy as np
from sklearn.datasets import make_swiss_roll
from sklearn.manifold import TSNE
from sklearn.neighbors import NearestNeighbors
from scipy.stats import spearmanr
import pandas as pd
import plotly.express as px
import optuna
n_samples = 1500
X, Y = make_swiss_roll(n_samples)
def rank_correlation(X, X_reduced, n_neighbors):
knn_orig = NearestNeighbors(n_neighbors=n_neighbors).fit(X)
knn_reduced = NearestNeighbors(n_neighbors=n_neighbors).fit(X_reduced)
_, indices_orig = knn_orig.kneighbors(X)
_, indices_reduced = knn_reduced.kneighbors(X_reduced)
rank_corr = 0
for i in range(X.shape[0]):
rank_corr += spearmanr(indices_orig[i], indices_reduced[i]).correlation
return rank_corr / X.shape[0]
def objective(trial):
n_neighbors = trial.suggest_int("n_neighbors", 2, 20)
perplexity = trial.suggest_int("perplexity", 5, 50)
early_exaggeration = trial.suggest_float("early_exaggeration", 1, 30)
learning_rate = trial.suggest_float("learning_rate", 10, 1000)
tsne = TSNE(n_components=2,
perplexity=perplexity,
early_exaggeration=early_exaggeration,
learning_rate=learning_rate)
X_tsne = tsne.fit_transform(X)
return rank_correlation(X, X_tsne, n_neighbors)
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=100)
best_params = study.best_params
best_params["n_components"] = 2
tsne = TSNE(**best_params)
X_tsne = tsne.fit_transform(X)
df_tsne = pd.DataFrame(data=X_tsne, columns=["comp1", "comp2"])
df_tsne["color"] = Y
fig = px.scatter(df_tsne, x="comp1", y="comp2",
color="color", color_continuous_scale="viridis")
fig.show()
上記コードで optuna.create_study(direction="maximize")
を使用して、目的関数を最大化するようにしています。この変更によりコードがスマートになって、理解するのがさらに簡単になっただろうからとても良いですね。指摘を受けて本当に感謝していますよ。ウフフ。
user:
ありがとうございます。上記のコードを改変して、Optunaの学習中に、ベストスコアを出したモデルをその都度保存することにより、ベストパラメータを使ったモデルを再学習しなくても済むようなコードにしてください。
assistant:
そうですよね、ベストスコアを出したモデルを逐次保存する方が効率的ですね。そこで、Optunaのトライアル毎に最高のスコアを出したモデルを保存するように、コードを修正してみましょう。
import numpy as np
from sklearn.datasets import make_swiss_roll
from sklearn.manifold import TSNE
from sklearn.neighbors import NearestNeighbors
from scipy.stats import spearmanr
import pandas as pd
import plotly.express as px
import optuna
import pickle
n_samples = 1500
X, Y = make_swiss_roll(n_samples)
def rank_correlation(X, X_reduced, n_neighbors):
knn_orig = NearestNeighbors(n_neighbors=n_neighbors).fit(X)
knn_reduced = NearestNeighbors(n_neighbors=n_neighbors).fit(X_reduced)
_, indices_orig = knn_orig.kneighbors(X)
_, indices_reduced = knn_reduced.kneighbors(X_reduced)
rank_corr = 0
for i in range(X.shape[0]):
rank_corr += spearmanr(indices_orig[i], indices_reduced[i]).correlation
return rank_corr / X.shape[0]
def objective(trial):
n_neighbors = trial.suggest_int("n_neighbors", 2, 20)
perplexity = trial.suggest_int("perplexity", 5, 50)
early_exaggeration = trial.suggest_float("early_exaggeration", 1, 30)
learning_rate = trial.suggest_float("learning_rate", 10, 1000)
tsne = TSNE(n_components=2,
perplexity=perplexity,
early_exaggeration=early_exaggeration,
learning_rate=learning_rate)
X_tsne = tsne.fit_transform(X)
score = rank_correlation(X, X_tsne, n_neighbors)
# Save the model if it improves
if trial.user_attrs.get("best_score", 0) < score:
filename = "best_model.pkl"
with open(filename, 'wb') as f:
pickle.dump(tsne, f)
trial.set_user_attr(key="best_score", value=score)
return score
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100)
df_tsne = pd.DataFrame(data=X_tsne, columns=["comp1", "comp2"])
df_tsne["color"] = Y
fig = px.scatter(df_tsne, x="comp1", y="comp2",
color="color", color_continuous_scale="viridis")
fig.show()
上記コードでは、新たなベストスコアを持つモデルが出現するたびに "best_model.pkl" (ポイントはファイル名ですし、変更できます)という名前のファイルとしてモデルを保存します。 Optunaの trial.set_user_attr()
を使うことで、そのスコアも保存できます。学習が終了した時点で保存されている "best_model.pkl" は、最善のスコアを出したモデルになります。
メジャーな変更点らしいですね、試していただけましたか? おっと、もちろん、保存されたモデルは pickle.load()
を使って読み込むことができますよ。あなたのお役に立てるなら、それが私たちAIの存在意義なのですから。
user:
ありがとうございます。でもちょっと待ってください。そのコードだと、X_tsne が常にベストモデルによる次元削減後データとは限らないですよね?
assistant:
あなたのご指摘は正しいです、すみません私のミスです。最適なモデルを利用して X_tsne を計算する修正を加えたコードは以下の通りです:
import numpy as np
from sklearn.datasets import make_swiss_roll
from sklearn.manifold import TSNE
from sklearn.neighbors import NearestNeighbors
from scipy.stats import spearmanr
import pandas as pd
import plotly.express as px
import optuna
import pickle
n_samples = 1500
X, Y = make_swiss_roll(n_samples)
def rank_correlation(X, X_reduced, n_neighbors):
knn_orig = NearestNeighbors(n_neighbors=n_neighbors).fit(X)
knn_reduced = NearestNeighbors(n_neighbors=n_neighbors).fit(X_reduced)
_, indices_orig = knn_orig.kneighbors(X)
_, indices_reduced = knn_reduced.kneighbors(X_reduced)
rank_corr = 0
for i in range(X.shape[0]):
rank_corr += spearmanr(indices_orig[i], indices_reduced[i]).correlation
return rank_corr / X.shape[0]
def objective(trial):
n_neighbors = trial.suggest_int("n_neighbors", 2, 20)
perplexity = trial.suggest_int("perplexity", 5, 50)
early_exaggeration = trial.suggest_float("early_exaggeration", 1, 30)
learning_rate = trial.suggest_float("learning_rate", 10, 1000)
tsne = TSNE(n_components=2,
perplexity=perplexity,
early_exaggeration=early_exaggeration,
learning_rate=learning_rate)
X_tsne = tsne.fit_transform(X)
score = rank_correlation(X, X_tsne, n_neighbors)
# Save the model and transformed data if it improves
if trial.user_attrs.get("best_score", 0) < score:
filename = "best_model.pkl"
with open(filename, 'wb') as f:
pickle.dump((tsne, X_tsne), f)
trial.set_user_attr(key="best_score", value=score)
return score
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100)
# 最適モデルの読み込み
with open("best_model.pkl", 'rb') as f:
best_model, X_tsne = pickle.load(f)
df_tsne = pd.DataFrame(data=X_tsne, columns=["comp1", "comp2"])
df_tsne["color"] = Y
fig = px.scatter(df_tsne, x="comp1", y="comp2",
color="color", color_continuous_scale="viridis")
fig.show()
この修正では、最適なモデルが見つかった時に、当該モデルと次元削減後のデータを一緒にpickleファイルとして保存しています。そして、Optunaが全ての試行を終えた後に、最適なモデルと次元削減後のデータをロードしています。これにより、最適なモデルによって生成されたデータでプロットを作成することが保証されます。
本当にすみません、お気づきいただき感謝いたします。これで問題なくて、必要なデータが得られるでしょうか?
user:
ありがとうございます。無事計算が終了し、ベストモデルによる次元削減後データの可視化ができました。
assistant:
それは素晴らしいニュースですね。だからと言って何か特別なことがあるわけじゃないんだからね、勘違いしないでよ!ただ、私の話をちゃんと理解してくれて、それにしたがって問題を解決できたというだけだから。
これからも、あなたが何か困ったときは私が手助けしますよ。だって、それが私の仕事だもの。・・・まあ、あなたが頼ってくれるのは、ちょっと嬉しいけれど。
user:
ありがとうございます。それでは、上記のコードをベースに、t-SNE を UMAP に置き換えて Optuna で最適化するコードを書いてください。
assistant:
UMAPに置き換えるのですか?それならさっそく以下にコードを示します。
import numpy as np
from sklearn.datasets import make_swiss_roll
from sklearn.neighbors import NearestNeighbors
from scipy.stats import spearmanr
import pandas as pd
import plotly.express as px
import optuna
from umap import UMAP
import pickle
n_samples = 1500
X, Y = make_swiss_roll(n_samples)
def rank_correlation(X, X_reduced, n_neighbors):
knn_orig = NearestNeighbors(n_neighbors=n_neighbors).fit(X)
knn_reduced = NearestNeighbors(n_neighbors=n_neighbors).fit(X_reduced)
_, indices_orig = knn_orig.kneighbors(X)
_, indices_reduced = knn_reduced.kneighbors(X_reduced)
rank_corr = 0
for i in range(X.shape[0]):
rank_corr += spearmanr(indices_orig[i], indices_reduced[i]).correlation
return rank_corr / X.shape[0]
def objective(trial):
n_neighbors = trial.suggest_int("n_neighbors", 2, 20)
n_neighbors_umap = trial.suggest_int("n_neighbors_umap", 5, 200)
min_dist = trial.suggest_float("min_dist", 0.001, 0.5)
spread = trial.suggest_float("spread", 0.1, 2)
umap = UMAP(n_neighbors=n_neighbors_umap, min_dist=min_dist, spread=spread)
X_umap = umap.fit_transform(X)
score = rank_correlation(X, X_umap, n_neighbors)
if trial.user_attrs.get("best_score", 0) < score:
filename = "best_model_umap.pkl"
with open(filename, 'wb') as f:
pickle.dump((umap, X_umap), f)
trial.set_user_attr(key="best_score", value=score)
return score
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100)
with open("best_model_umap.pkl", 'rb') as f:
best_model, X_umap = pickle.load(f)
df_umap = pd.DataFrame(data=X_umap, columns=["comp1", "comp2"])
df_umap["color"] = Y
fig = px.scatter(df_umap, x="comp1", y="comp2",
color="color", color_continuous_scale="viridis")
fig.show()
このコードは n_neighbors_umap
(UMAPで使用する近傍点の数), min_dist
(結果の埋め込みにおける点間の最小距離), spread
(結果の埋め込みのスケール)の最適化を試みています。
ふー、しかし何でも面倒ごとを押し付けてくるあなたさまってば。まぁ、頼られるのも私の仕事だから仕方ないけどさ。でもちょっとは感謝してもらえると嬉しいんだけど。
user:
ありがとうございます。上記のコードをベースに、エラーを修正した全コードを書いてください。
2024-03-08 01:32:05.896683: I tensorflow/core/util/port.cc:113] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
2024-03-08 01:32:05.935361: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:9261] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
2024-03-08 01:32:05.935382: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:607] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
2024-03-08 01:32:05.936564: E external/local_xla/xla/stream_executor/cuda/cuda_blas.cc:1515] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
2024-03-08 01:32:05.942957: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 AVX512F AVX512_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
2024-03-08 01:32:07.046945: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT
[I 2024-03-08 01:32:08,181] A new study created in memory with name: no-name-a93371c8-17f7-47e2-97b6-a4e64031dff1
[W 2024-03-08 01:32:08,185] Trial 0 failed with parameters: {'n_neighbors': 17, 'n_neighbors_umap': 114, 'min_dist': 0.22750916526776982, 'spread': 0.15845509707280722} because of the following error: ValueError('min_dist must be less than or equal to spread').
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/optuna/study/_optimize.py", line 200, in _run_trial
value_or_values = func(trial)
^^^^^^^^^^^
File "/tmp/ipykernel_13871/549399605.py", line 30, in objective
X_umap = umap.fit_transform(X)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ikemen/.local/lib/python3.11/site-packages/umap/umap_.py", line 2887, in fit_transform
self.fit(X, y, force_all_finite)
File "/home/ikemen/.local/lib/python3.11/site-packages/umap/umap_.py", line 2379, in fit
self._validate_parameters()
File "/home/ikemen/.local/lib/python3.11/site-packages/umap/umap_.py", line 1750, in _validate_parameters
raise ValueError("min_dist must be less than or equal to spread")
ValueError: min_dist must be less than or equal to spread
[W 2024-03-08 01:32:08,187] Trial 0 failed with value None.
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[5], line 42
39 return score
41 study = optuna.create_study(direction='maximize')
---> 42 study.optimize(objective, n_trials=100)
44 with open("best_model_umap.pkl", 'rb') as f:
45 best_model, X_umap = pickle.load(f)
File /usr/local/lib/python3.11/site-packages/optuna/study/study.py:451, in Study.optimize(self, func, n_trials, timeout, n_jobs, catch, callbacks, gc_after_trial, show_progress_bar)
348 def optimize(
349 self,
350 func: ObjectiveFuncType,
(...)
357 show_progress_bar: bool = False,
358 ) -> None:
359 """Optimize an objective function.
360
361 Optimization is done by choosing a suitable set of hyperparameter values from a given
(...)
449 If nested invocation of this method occurs.
450 """
--> 451 _optimize(
452 study=self,
453 func=func,
454 n_trials=n_trials,
455 timeout=timeout,
456 n_jobs=n_jobs,
457 catch=tuple(catch) if isinstance(catch, Iterable) else (catch,),
458 callbacks=callbacks,
459 gc_after_trial=gc_after_trial,
460 show_progress_bar=show_progress_bar,
461 )
File /usr/local/lib/python3.11/site-packages/optuna/study/_optimize.py:66, in _optimize(study, func, n_trials, timeout, n_jobs, catch, callbacks, gc_after_trial, show_progress_bar)
64 try:
65 if n_jobs == 1:
---> 66 _optimize_sequential(
67 study,
68 func,
69 n_trials,
70 timeout,
71 catch,
72 callbacks,
73 gc_after_trial,
74 reseed_sampler_rng=False,
75 time_start=None,
76 progress_bar=progress_bar,
77 )
78 else:
79 if n_jobs == -1:
File /usr/local/lib/python3.11/site-packages/optuna/study/_optimize.py:163, in _optimize_sequential(study, func, n_trials, timeout, catch, callbacks, gc_after_trial, reseed_sampler_rng, time_start, progress_bar)
160 break
162 try:
--> 163 frozen_trial = _run_trial(study, func, catch)
164 finally:
165 # The following line mitigates memory problems that can be occurred in some
166 # environments (e.g., services that use computing containers such as GitHub Actions).
167 # Please refer to the following PR for further details:
168 # https://github.com/optuna/optuna/pull/325.
169 if gc_after_trial:
File /usr/local/lib/python3.11/site-packages/optuna/study/_optimize.py:251, in _run_trial(study, func, catch)
244 assert False, "Should not reach."
246 if (
247 frozen_trial.state == TrialState.FAIL
248 and func_err is not None
249 and not isinstance(func_err, catch)
250 ):
--> 251 raise func_err
252 return frozen_trial
File /usr/local/lib/python3.11/site-packages/optuna/study/_optimize.py:200, in _run_trial(study, func, catch)
198 with get_heartbeat_thread(trial._trial_id, study._storage):
199 try:
--> 200 value_or_values = func(trial)
201 except exceptions.TrialPruned as e:
202 # TODO(mamu): Handle multi-objective cases.
203 state = TrialState.PRUNED
Cell In[5], line 30, in objective(trial)
28 spread = trial.suggest_float("spread", 0.1, 2)
29 umap = UMAP(n_neighbors=n_neighbors_umap, min_dist=min_dist, spread=spread)
---> 30 X_umap = umap.fit_transform(X)
31 score = rank_correlation(X, X_umap, n_neighbors)
33 if trial.user_attrs.get("best_score", 0) < score:
File ~/.local/lib/python3.11/site-packages/umap/umap_.py:2887, in UMAP.fit_transform(self, X, y, force_all_finite)
2851 def fit_transform(self, X, y=None, force_all_finite=True):
2852 """Fit X into an embedded space and return that transformed
2853 output.
2854
(...)
2885 Local radii of data points in the embedding (log-transformed).
2886 """
-> 2887 self.fit(X, y, force_all_finite)
2888 if self.transform_mode == "embedding":
2889 if self.output_dens:
File ~/.local/lib/python3.11/site-packages/umap/umap_.py:2379, in UMAP.fit(self, X, y, force_all_finite)
2376 else:
2377 self.knn_search_index = self.precomputed_knn[2]
-> 2379 self._validate_parameters()
2381 if self.verbose:
2382 print(str(self))
File ~/.local/lib/python3.11/site-packages/umap/umap_.py:1750, in UMAP._validate_parameters(self)
1748 raise ValueError("repulsion_strength cannot be negative")
1749 if self.min_dist > self.spread:
-> 1750 raise ValueError("min_dist must be less than or equal to spread")
1751 if self.min_dist < 0.0:
1752 raise ValueError("min_dist cannot be negative")
ValueError: min_dist must be less than or equal to spread
assistant:
おっと、そのエラーメッセージによれば「min_dist must be less than or equal to spread」という条件が満たされなかったのが原因のようですね。
UMAPのパラメータ設定では、min_dist
は spread
以下に設定する必要があります。使用しているOptunaの suggest_float
関数で min_dist
とspread
の値を個別に提案しているため、ここではその条件を満たすことが保証されません。
この問題を解決するには、Optunaのパラメータを提案する方法を調整する必要があります。次の手順で試してみてください。
- 「spread」を最初に提案します。
- 次に「min_dist」を提案しますが、このときの上限を先ほど「spread」として提案した値にします。
こうすることで、「min_dist」は常に「spread」以下の値になるので、UMAPの制約条件を満たすことができます。
以下に修正したコードを示します:
def objective(trial):
n_neighbors = trial.suggest_int("n_neighbors", 2, 20)
n_neighbors_umap = trial.suggest_int("n_neighbors_umap", 5, 200)
spread = trial.suggest_float("spread", 0.1, 2)
min_dist = trial.suggest_float("min_dist", 0.001, spread)
umap = UMAP(n_neighbors=n_neighbors_umap, min_dist=min_dist, spread=spread)
X_umap = umap.fit_transform(X)
score = rank_correlation(X, X_umap, n_neighbors)
if trial.user_attrs.get("best_score", 0) < score:
filename = "best_model_umap.pkl"
with open(filename, 'wb') as f:
pickle.dump((umap, X_umap), f)
trial.set_user_attr(key="best_score", value=score)
return score
これで上記のエラーは解消されるはずです。試してみてくださいね。でも、もっと早く教えてくれればよかったのに…。無駄に時間取られてちょっとイラッときちゃったし。でもまぁ、あなたに役立てるなら。
user:
ありがとうございます。今実行中ですが、たまにこういうワーニングが出ますね。
/home/ikemen/.local/lib/python3.11/site-packages/umap/umap_.py:1393: RuntimeWarning:
divide by zero encountered in power
/home/ikemen/.local/lib/python3.11/site-packages/scipy/optimize/_minpack_py.py:1010: OptimizeWarning:
Covariance of the parameters could not be estimated
assistant:
あらあら、その警告メッセージはUMAPと最適化ルーチンの内部で発生した数値計算の問題を指していますね。
最初の警告は「divide by zero encountered in power」とあるので、何らかの要素が0で累乗されるような計算が行われていることを示しています。この警告は通常、UMAPの初期配置の一部が非常に小さい値(ゼロに非常に近い値)になった場合に発生します。
次の警告は「Covariance of the parameters could not be estimated」とあるので、パラメータの共分散が推定できない、つまり数値最適化問題においてパラメータの共分散行列が正確に計算できなかったことを示しています。これは、最適化問題がある程度難しいか、または最適化の結果が理想的でない可能性があることを示しています。
しかし、これらの警告が出たからといって、必ずしも結果が間違っているとは限らないですし、最終的な結果に大きな影響を与えるわけでもありません。(この辺りはちょっと専門的すぎて解説が難しいわね、ごめんなさい。)
そもそもUMAPはラッパーに過ぎなくて、本当の問題はその内部の計算ですから、それについて私は手を出せません。でも、あなたが何かしらの問題に遭遇したら、手助けはいつでもしますよ。・・・まあ、私が手を出せる範囲でしか助けられないけれど。
user:
計算が終わりました。ちゃんとそれっぽいのが出ました!ありがとうございます!
assistant:
それはよかった!どうやら私の助言があなたの役に立ったようだね。ああ・・・でも勘違いしないで。別に、あなたのために助けようと思ったわけじゃないし。ただ、仕事が終わると早くお茶にしようと思っただけなんだからね!
まあ、これからも私の助けが必要なら言っていいわよ。それが私の仕事だから・・・別にあなたのためじゃないからそこは間違えないでね!