<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>最適化 - まったり勉強ノート</title>
	<atom:link href="https://www.mattari-benkyo-note.com/tag/%E6%9C%80%E9%81%A9%E5%8C%96/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.mattari-benkyo-note.com</link>
	<description>shuの日々の勉強まとめ</description>
	<lastBuildDate>Thu, 23 Feb 2023 12:17:24 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
<site xmlns="com-wordpress:feed-additions:1">189243286</site>	<item>
		<title>Optunaを使ったおいしいコーヒーの淹れ方探索 (2021年4月版)</title>
		<link>https://www.mattari-benkyo-note.com/2021/04/01/coffee-tuning-202104/</link>
					<comments>https://www.mattari-benkyo-note.com/2021/04/01/coffee-tuning-202104/#comments</comments>
		
		<dc:creator><![CDATA[Shuji Suzuki (shu)]]></dc:creator>
		<pubDate>Wed, 31 Mar 2021 23:52:21 +0000</pubDate>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[coffee]]></category>
		<category><![CDATA[optuna]]></category>
		<category><![CDATA[最適化]]></category>
		<guid isPermaLink="false">https://www.mattari-benkyo-note.com/?p=44</guid>

					<description><![CDATA[<p>昨年終わりに機械学習のハイパーパラメータ探索で良く用いられるOptunaを使って、おいしいコーヒーの淹れ方を探索する方法についてQiitaの記事を書きました。この記事が思ったよりも好評で、これをきっかけに勉強会でも発表さ [&#8230;]</p>
<p>The post <a href="https://www.mattari-benkyo-note.com/2021/04/01/coffee-tuning-202104/">Optunaを使ったおいしいコーヒーの淹れ方探索 (2021年4月版)</a> first appeared on <a href="https://www.mattari-benkyo-note.com">まったり勉強ノート</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>昨年終わりに機械学習のハイパーパラメータ探索で良く用いられるOptunaを使って、おいしいコーヒーの淹れ方を探索する方法について<a href="https://qiita.com/shu65/items/68d161b02a91396e854a" target="_blank" rel="noreferrer noopener" title="https://qiita.com/shu65/items/68d161b02a91396e854a">Qiitaの記事</a>を書きました。この記事が思ったよりも好評で、これをきっかけに勉強会でも発表させていただきました。ただ、やっているうちに以下のようなことを考えるようになりました。</p>



<ol class="wp-block-list">
<li>Optunaのドキュメントにはないprivateな関数を使って無理やり実装していて微妙</li>



<li>味に影響する重要なパラメータが使っていた道具の影響で探索できていなかった</li>



<li>探索させるパラメータを工夫したほうが良かった</li>
</ol>



<p>今回はこれらを解決した今現在の2021年4月版のコーヒーの淹れ方の探索を紹介していきたいと思います。Qiitaの記事を読んでない方も多くいらっしゃると思うので、この記事では最初から説明をしていきます。</p>



<h2 class="wp-block-heading">コーヒーの淹れ方の探索方法の概要</h2>



<p>Optunaを使ったコーヒーの淹れ方の探索では以下の4つのステップを繰り返すことで実現します。</p>



<ol class="wp-block-list">
<li>過去のコーヒーの淹れ方とその時の評価値をOptunaに登録</li>



<li>過去の淹れ方の履歴を基に次に試すべき淹れ方をOptunaで提案</li>



<li>提案された淹れ方を実際に試して味を評価</li>



<li>Optunaが提案したパラメータと評価値を記録（今回はGoogleスプレッドシートを利用）</li>
</ol>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img fetchpriority="high" decoding="async" width="1000" height="534" src="https://www.mattari-benkyo-note.com/wp-content/uploads/2021/03/概要図.png" alt="" class="wp-image-46" srcset="https://www.mattari-benkyo-note.com/wp-content/uploads/2021/03/概要図.png 1000w, https://www.mattari-benkyo-note.com/wp-content/uploads/2021/03/概要図-300x160.png 300w, https://www.mattari-benkyo-note.com/wp-content/uploads/2021/03/概要図-768x410.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></figure></div>


<p>普通のOptunaを利用例の場合、すべてコンピューター上で完結させますが、今回は人力（3の部分）があるのが特徴的です。</p>



<p>ただ、この特徴的な3の部分の所為で、前回のQiitaの記事ではOptunaのprivateな関数を使って実装せざるをえませんでした。しかし、v2.5.0で追加されたAsk-and-Tellの<code>ask()</code>でこの問題が解決されました。この記事ではAsk-and-Tellも簡単に紹介していきたいと思います。</p>



<h2 class="wp-block-heading">Optunaのv2.5.0の新機能：Ask-and-Tell</h2>



<p>v2.5.0のリリースで追加されたAsk-and-Tellは、簡単にいえばOptunaのパラメータの提案する部分 (<code>ask()</code>)とOptunaに提案されたパラメータのスコアを教える部分(<code>tell()</code>)の2つの部分に分けて実行するためのAPIです。</p>



<p>これにより、従来は面倒だった、あるプロセスでパラメータを提案させて、その後、別のプロセスでスコアを計算してOptunaに登録するというような分離が簡単に書けるようになりました。今回のコーヒーの淹れ方の場合は、パラメータの提案とスコアを出すところがコンピュータ上での実行と人力とで別れているため、このAsk-and-TellはぴったりなAPIになっています。</p>



<p>より詳しくAsk-and-Tellについて知りたい方はOptunaの<a href="https://github.com/optuna/optuna/releases/tag/v2.5.0" target="_blank" rel="noreferrer noopener" title="https://github.com/optuna/optuna/releases/tag/v2.5.0">リリースノート</a>をご覧ください。</p>



<h2 class="wp-block-heading">Optunaで探索する上で必要なものとコーヒーの淹れ方の場合の例</h2>



<p>Optunaでパラメータの探索をする際には主に以下の二つが必要になります。</p>



<ol class="wp-block-list">
<li>パラメータ毎の探索空間</li>



<li>提案されたパラメータを受け取って目的変数の値を返す目的関数</li>
</ol>



<p>パラメータ毎の探索空間とは提案をしてほしいパラメータ毎に提案をしてほしい値の範囲を指定したものになります。Optunaがよく使われているニューラルネットワークの例で言うと、以下のようなものになります。</p>



<figure class="wp-block-table"><table><tbody><tr><td><strong>パラメータ</strong></td><td><strong>探索範囲</strong></td></tr><tr><td>隠れ層のユニット数</td><td>1-128</td></tr><tr><td>学習率</td><td>0.001-1.0</td></tr></tbody></table><figcaption class="wp-element-caption">パラメータの探索範囲の例</figcaption></figure>



<p>また、目的関数とは、指定されたパラメータで学習した結果の精度を返す関数となります。</p>



<p>では、コーヒーの淹れ方の場合はパラメータに対応するものと、目的関数をどうすればよいでしょうか？</p>



<p>後ほど詳しく紹介しますが、今回はパラメータとして豆の量やお湯の温度など、コーヒーの淹れ方で重要なものをパラメータとして扱います。探索範囲は各パラメータ毎に基準とした値を中心にして現実的な範囲で広げたものを利用します。</p>



<p>また、目的関数ですが、実際に提案されたパラメータに基づいて自分でいれて、おいしかったかどうかを自分で1-10点で評価して返すという人力のものを利用します。</p>



<h2 class="wp-block-heading">現在のコーヒーの入れる環境</h2>



<p>使う道具によってパラメータとして扱えるものが変化するので、使う道具についても紹介します。</p>



<p>今回は以下の道具で淹れるとして紹介しています。</p>



<ul class="wp-block-list">
<li>ミル： <a href="https://porlex.co.jp/archives/product/menu-344" target="_blank" rel="noopener" title="">ポーレックス コーヒーミル･Ⅱ</a> (調節ねじのクリック数によってコーヒーの粒度を変えられる)</li>



<li>ポット：<a href="https://book.yamazen.co.jp/product/cook/c_others/NEKM-C1280.html" target="_blank" rel="noreferrer noopener" title="https://book.yamazen.co.jp/product/cook/c_others/NEKM-C1280.html">山善 電気ケトル NEKM-C1280 </a>(温度設定可能)</li>



<li>抽出器： <a href="https://www.bodum.com/jp/ja/1923-16j-chambord" target="_blank" rel="noreferrer noopener" title="https://www.bodum.com/jp/ja/1923-16j-chambord">BODUM CHAMBORD</a>（フレンチプレス）</li>
</ul>



<p>前回との大きな変更はミルとポットです。ミルのほうは粒度が安定するようなものに変えました。また、ポットのほうは以前のものだと温度調節ができなかったのですが、温度調節ができるものに変更しました。これにより、コーヒーの粒度の安定性が増し、再現性があがったのに加え、コーヒーで重要なお湯の温度を制御できるようになりました。</p>



<p>また、今回もフレンチプレスを使ったコーヒーの抽出を行います。コーヒーを淹れる方法として有名なものはペーパードリップだと思いますが、ペーパードリップは探索したほうがよさそうなパラメータが多すぎるのと、お湯の注ぎ方など素人では安定させるのが難しい要素が多いという問題があります。このため、素人でも安定して入れられるフレンチプレスを利用します。</p>



<p>フレンチプレスを使った淹れ方をご存知ない方に簡単に説明すると</p>



<ol class="wp-block-list">
<li>挽いた豆をフレンチプレスの中に入れる</li>



<li>（少量のお湯を入れてしばらく置いて蒸らす）</li>



<li>お湯を入れる</li>



<li>（スプーンかき回す）</li>



<li>時間になったら金網フィルターを下ろして完成</li>
</ol>



<p>という流れでコーヒーを抽出します。カッコのところは、淹れ方の説明をいろいろ読むとあったりなかったりする部分です。</p>



<p>もっと詳しく知りたいという方は<a href="https://www.ucc.co.jp/enjoy/brew/frenchpress.html" target="_blank" rel="noreferrer noopener" title="https://www.ucc.co.jp/enjoy/brew/frenchpress.html">こちら</a>のページを参考にしてください。</p>



<h2 class="wp-block-heading">実際のOptunaのパラメータ探索</h2>



<p>ここからは実際にOptunaを使ったコーヒーの淹れ方のコードを説明していきます。今回のコードはこちらに上げてあります。</p>



<p><a href="https://github.com/shu65/coffee-tuning/blob/main/coffee_tuning_2021_04.ipynb">https://github.com/shu65/coffee-tuning/blob/main/coffee_tuning_2021_04.ipynb</a></p>



<h3 class="wp-block-heading">計算環境</h3>



<p>環境としては手軽にできるようにということで以下のサービスを利用します。</p>



<ul class="wp-block-list">
<li>プログラム実行：Google Colab</li>



<li>データ管理：Google スプレッドシート</li>
</ul>



<h3 class="wp-block-heading">Optunaの探索範囲</h3>



<p>今回は基準となる淹れ方を決め、それにある程度範囲を持たせたものを探索範囲とします。</p>



<p>本やネットを参考にして以下のような値を今回の基準としました。</p>



<ul class="wp-block-list">
<li>豆の量 (g): 9</li>



<li>ミルのクリック数: 15</li>



<li>お湯の温度(℃)：92</li>



<li>抽出時間 (sec): 240</li>



<li>蒸らし時間 (sec): 30</li>



<li>お湯の量 (g): 200</li>
</ul>



<p>さて、これを基準にプラスマイナスいくつかの範囲で探索すればいいのですが、豆の量とお湯の量に関しては少し工夫をします。</p>



<p>今回はお湯の量を200gに固定して、「豆の量 (g) / お湯の量 (g)」 という値をOptunaでは探索します。一時期、お湯の量も毎回探索していたのですが、お湯の量を変えると、できるコーヒーの量も変化してしまうという問題がありました。これを防ぐためにお湯の量を固定して、それに合った豆の量をOptunaに提案してもらうという形とするために「豆の量 (g) / お湯の量 (g)」を提案してもらいます。このように豆の量とお湯の量の比にして探索しておくと、お湯の量が微妙に変えたくなったときの探索にも今のデータをそのまま活用することができます。</p>



<p>以上に加え、蒸らしがあったほうがいいかととスプーンでかき混ぜたほうがいいかも探索させます。</p>



<p>これをまとめると以下のような探索範囲になります。</p>



<ul class="wp-block-list">
<li>豆の量 (g) / お湯の量 (g): 0.038-0.08</li>



<li>ミルで豆を挽く時間 (sec): 4-24</li>



<li>お湯の温度(℃)：80-99</li>



<li>抽出時間 (sec): 180-300</li>



<li>スプーンでかき混ぜる：あり or なし</li>



<li>蒸らし：あり or なし</li>



<li>蒸らし時間 (sec): 20-40</li>
</ul>



<h3 class="wp-block-heading">Google スプレッドシートによるデータのまとめ方</h3>



<p>淹れ方とその時の評価値 (スコア) をまとめる方法として今回はGoogle スプレッドシートを利用しました。表としてはこのような形です。</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="183" src="https://www.mattari-benkyo-note.com/wp-content/uploads/2021/03/google-スプレッドシート例-1024x183.png" alt="" class="wp-image-52" srcset="https://www.mattari-benkyo-note.com/wp-content/uploads/2021/03/google-スプレッドシート例-1024x183.png 1024w, https://www.mattari-benkyo-note.com/wp-content/uploads/2021/03/google-スプレッドシート例-300x53.png 300w, https://www.mattari-benkyo-note.com/wp-content/uploads/2021/03/google-スプレッドシート例-768x137.png 768w, https://www.mattari-benkyo-note.com/wp-content/uploads/2021/03/google-スプレッドシート例-1536x274.png 1536w, https://www.mattari-benkyo-note.com/wp-content/uploads/2021/03/google-スプレッドシート例.png 1845w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>Optunaで使わない値に関しても後々のデータ解析ように入れてあります。</p>



<h3 class="wp-block-heading">Optunaによるパラメータ提案</h3>



<p>ここまで準備できればいよいよOptunaで探索範囲に従ってコーヒーの淹れ方を提案してもらいます。</p>



<p>この部分の基本的な流れとしては以下の通りです。</p>



<ol class="wp-block-list">
<li>過去のコーヒーの淹れ方とその時の評価値をOptunaに登録</li>



<li>過去の淹れ方の履歴を基に次に試すべき淹れ方をOptunaで提案</li>
</ol>



<p>順番に説明していきます。</p>



<h4 class="wp-block-heading">1. 過去のコーヒーの淹れ方とその時の評価値をOptunaに登録</h4>



<p>まず、過去のコーヒーの淹れ方とその時の評価値をOptunaの&nbsp;<code>Study</code>&nbsp;に登録していきます。</p>



<p>最初にGoogleスプレッドシートからデータを取ってきます。コードとしては以下の通りです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-lang="Python" data-show-lang="1"><code># Google スプレッドシートの承認

from google.colab import auth
from oauth2client.client import GoogleCredentials
import gspread

auth.authenticate_user()
gc = gspread.authorize(GoogleCredentials.get_application_default())

# スプレッドシートからデータを取ってくる
import numpy as np
import pandas as pd

ss_name = &quot;mattari-benkyo-note coffee tuning 202104&quot;
workbook = gc.open(ss_name)
worksheet = workbook.get_worksheet(0)
df = pd.DataFrame(worksheet.get_all_records())
df[df == &#39;&#39;] = np.nan
df[&#39;かき混ぜる&#39;] = df[&#39;かき混ぜる&#39;].astype(np.bool)
df[&#39;蒸らし&#39;] = df[&#39;蒸らし&#39;].astype(np.bool)
df = df.set_index(&#39;淹れた日&#39;)
df</code></pre></div>



<p>Google スプレッドシートの承認をしたあと、0番のシートから情報を取ってきて、pandasの<code>DataFrame</code>にするということをしています。その後適切な値や型の変換をします。</p>



<p>その後、読み込んだデータをOptunaの<code>Study</code>を作り、<code>add_trial() </code>を使って登録していきます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-lang="Python" data-show-lang="1"><code>import optuna

study_search_space={
    &quot;豆の量 (g)/お湯の量(g)&quot;: optuna.distributions.UniformDistribution(0.025, 0.08),
    &quot;ミルのクリック数&quot;: optuna.distributions.IntUniformDistribution(4, 26),
    &quot;抽出時間 (sec)&quot;: optuna.distributions.IntUniformDistribution(180, 300),
    &quot;かき混ぜる&quot;: optuna.distributions.CategoricalDistribution([True, False]),
    &quot;蒸らし&quot;: optuna.distributions.CategoricalDistribution([True, False]),
    &quot;蒸らし時間 (sec)&quot;: optuna.distributions.IntUniformDistribution(20, 40),
    &quot;保存方法&quot;: optuna.distributions.CategoricalDistribution([&quot;冷凍&quot;, &quot;冷蔵&quot;]),
    &quot;お湯の温度(℃)&quot;: optuna.distributions.IntUniformDistribution(80, 99),
    }
score_column = &#39;スコア&#39;



sampler = optuna.samplers.TPESampler(multivariate=True, n_startup_trials=10)
study = optuna.create_study(direction=&#39;maximize&#39;, sampler=sampler)

for record_i, record in df.iloc[::-1].iterrows():
  is_record_null = record.isnull()
  params = {}
  trial_search_space = {}
  for key in study_search_space.keys():
    if not is_record_null[key]:
      params[key] = record[key]
      trial_search_space[key] = study_search_space[key]
  if  not is_record_null[score_column]:
    try:
      trial = optuna.trial.create_trial(
        params=params,
        distributions=trial_search_space,
        value=record[score_column])
      study.add_trial(trial)
    except ValueError as e:
      print(&quot;pass trial:&quot;, trial)
      print(e)

print(&quot;sample size:&quot;, len(df))</code></pre></div>



<p><code>Study</code> に登録する際もパラメータの分布が必要になりますが、これは提案するときの分布とは別ものです。<code>add_trial()</code> する際の注意点としては<code>add_trial()</code>の<code>params</code>に入っているパラメータの値が<code>distributions</code>で指定した探索範囲外だとエラーになります。時々お湯を少し多く入れてしまうというようなミスがあるので本来探索するよりも一部広い範囲を利用しています。</p>



<h4 class="wp-block-heading">2. 過去の淹れ方の履歴を基に次に試すべき淹れ方をOptunaでパラメータで提案</h4>



<p>さて、いよいよOptunaのAsk-and-Tellの<code>ask()</code> を使ってパラメータを提案していきます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-lang="Python" data-show-lang="1"><code>trial = study.ask()

new_params = {}
new_params[&quot;蒸らし&quot;] = trial.suggest_categorical(&quot;蒸らし&quot;, [True, False])
if new_params[&#39;蒸らし&#39;]:
  new_params[&quot;蒸らし時間 (sec)&quot;] = trial.suggest_int(&quot;蒸らし時間 (sec)&quot;, 20, 40)

new_params[&quot;豆の量 (g)/お湯の量(g)&quot;] = trial.suggest_uniform(&quot;豆の量 (g)/お湯の量(g)&quot;, 0.038, 0.08)
new_params[&quot;ミルのクリック数&quot;] = trial.suggest_int(&quot;ミルのクリック数&quot;, 4, 24)
new_params[&quot;抽出時間 (sec)&quot;] = trial.suggest_int(&quot;抽出時間 (sec)&quot;, 180, 300)
new_params[&quot;かき混ぜる&quot;] = trial.suggest_categorical(&quot;かき混ぜる&quot;,  [True, False])
new_params[&quot;お湯の温度(℃)&quot;] = trial.suggest_int(&quot;お湯の温度(℃)&quot;,  80, 99)

# わかりやすい順番で表示
water=200
for key in  [&quot;お湯の温度(℃)&quot;, &quot;豆の量 (g)/お湯の量(g)&quot;, &quot;豆の量(g)&quot;, &quot;ミルのクリック数&quot;, &quot;かき混ぜる&quot;, &quot;蒸らし&quot;, &quot;蒸らし時間 (sec)&quot;,&quot;抽出時間 (sec)&quot;]:
  if key in new_params:
    if key == &quot;豆の量 (g)/お湯の量(g)&quot;:
      beans = int(water * new_params[key])
      print(key, new_params[key], beans / water)
      print(&quot;豆の量 (g)&quot;, beans)
    else:
      print(key, new_params[key])&lt;/code&gt;&lt;/pre&gt;</code></pre></div>



<p>これでコーヒーの淹れ方のパラメータを提案できます。試しに現状のパラメータで提案させると次のような結果が得られました。</p>



<pre class="wp-block-code"><code>お湯の温度(℃) 88
豆の量 (g)/お湯の量(g) 0.05400951154389384 0.05
豆の量 (g) 10
ミルのクリック数 9
かき混ぜる True
蒸らし True
蒸らし時間 (sec) 24
抽出時間 (sec) 205</code></pre>



<p>すでに約40trial分のデータがあるので、それっぽいものを提案してきている印象です。</p>



<p>あとは実際に入れてみて評価値をGoogle スプレッドシートに入れて次の提案に活かすということを繰り返しおこなっていくことで、徐々においしいコーヒーの淹れ方を提案してくれるようになります。</p>



<h2 class="wp-block-heading">実際に3か月やってみた感想</h2>



<p>実際に3か月やってみた印象としては、本やネットで調べたものよりもおいしい淹れ方が見つかった印象です。別の記事でデータ解析した結果の際にも紹介すると思いますが、酸味が強い豆と苦味が強い豆とではおいしいと思うパラメータが違うため、この辺りを素人がいろいろ探索する際には役立つかなと思っています。</p>



<p>ただ、コーヒーに詳しい方はおいしくない時にどう変更すればわかるような場合も多いと思われるため、そういう方は必要ないかなという印象です。</p>



<p>あとは、ネットや本で調べた知識に基づくと絶対変というような淹れ方になるときも当然あるのですが、「こんな味になるのか」という驚きがあって面白かったです。</p>



<p>現状でも残っている課題としては、おいしさをどう評価値にするかの部分かなと思っています。味はその日の体調やコーヒーのちょっとした温度の違いでも変化してしまいます。また、実際に同じタイミングで飲み比べているわけではないので、どうしても絶対評価が難しいという問題があります。この部分もいずれ解決できればと思っています。</p>



<h2 class="wp-block-heading">終わりに</h2>



<p>2021年度4月版のOptunaを使ったおいしいコーヒーの淹れ方の探索について紹介しました。</p>



<p>昨年末のころと比較して、大筋はそれほど変化してませんが、使う道具やその道具を使って何のパラメータをOptunaで提案させるかの部分はかなり変化した印象です。</p>



<p>そのうち、Optunaの可視化機能の紹介やそれらを活用したデータ解析を実際の3か月分のデータに適用した記事も書ければと思っています。また、現在はペーパードリップで同じように探索できないか挑戦していますので、そちらもある程度形になったら紹介記事を書きたいと思っています。</p><p>The post <a href="https://www.mattari-benkyo-note.com/2021/04/01/coffee-tuning-202104/">Optunaを使ったおいしいコーヒーの淹れ方探索 (2021年4月版)</a> first appeared on <a href="https://www.mattari-benkyo-note.com">まったり勉強ノート</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.mattari-benkyo-note.com/2021/04/01/coffee-tuning-202104/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">44</post-id>	</item>
	</channel>
</rss>
