図解機械学習: ニューラルネットワークと TensorFlow によるテキスト分類

図解機械学習: ニューラルネットワークと TensorFlow によるテキスト分類

開発者はよく、機械学習を始めたいなら、まずアルゴリズムを学ぶべきだと言います。しかし、それは私の経験ではありませんでした。

まず、アプリケーションがどのように動作するかを理解する必要があると言いました。これを理解すれば、アルゴリズムの内部の仕組みを詳しく調べることがはるかに簡単になります。

では、機械学習を理解するために直感力を養うにはどうすればよいでしょうか? 優れた方法の 1 つは、機械学習モデルを作成することです。

これらすべてのアルゴリズムをゼロから作成する方法がまだわからない場合は、すでにすべてのアルゴリズムを実装しているライブラリを使用できます。そのライブラリは TensorFlow です。

この記事では、テキストをカテゴリに分類する機械学習モデルを作成します。以下のトピックを取り上げます。

  1. TensorFlowの仕組み
  2. 機械学習モデルとは何か
  3. ニューラルネットワークとは何か
  4. ニューラルネットワークの学習方法
  5. データを操作してニューラルネットワークに渡す方法
  6. モデルを実行して予測結果を取得する方法

きっとたくさんの新しいことを学ぶことになるでしょうから、始めましょう!

テンソルフロー

TensorFlow は、Google が開発した機械学習用のオープンソース ライブラリです。ライブラリの名前は、その使用方法を理解するのに役立ちます。テンソルは、グラフのノードを通過する多次元配列です。

tf.グラフ

TensorFlow のすべての計算は、次の 2 種類の要素を持つデータ フロー グラフとして表されます。

  • tf.Operation の一種で、計算ユニットを表す
  • データ単位を表すtf.Tensorの型

これがどのように機能するかを確認するには、次のデータ フロー グラフを作成する必要があります。

(x+yを計算するグラフ)

x = [1,3,6]、y = [1,1,1]と定義する必要があります。グラフはデータ単位を表すために tf.Tensor を使用するため、定数 Tensor を作成する必要があります。

  1. テンソルフローをtfとしてインポートする
  2.  
  3. x = tf.定数([1,3,6])
  4.  
  5. y = tf.定数([1,1,1])

次に、操作単位を定義します。

  1. テンソルフローをtfとしてインポートする
  2.  
  3. x = tf.定数([1,3,6])
  4.  
  5. y = tf.定数([1,1,1])
  6.  
  7. op = tf.add (x,y)

図の要素がすべて揃いました。次にグラフを構築する必要があります。

  1. テンソルフローをtfとしてインポートする
  2.  
  3. my_graph = tf.Graph()
  4.  
  5. my_graph.as_default()の場合:
  6.  
  7. x = tf.定数([1,3,6])
  8.  
  9. y = tf.定数([1,1,1])
  10.  
  11. op = tf.add (x,y)

TensorFlow ワークフローは次のように動作します。まずグラフを作成し、次に計算 (操作を使用してグラフ ノードを実際に「実行」) します。グラフを実行するには、tf.Session を作成する必要があります。

tf.セッション

tf.Session オブジェクトは、Operation オブジェクトの実行環境をカプセル化します。テンソル オブジェクトが計算されます (ドキュメントより)。これを行うには、セッションでどのグラフを使用するかを定義する必要があります。

  1. テンソルフローをtfとしてインポートする
  2.  
  3. my_graph = tf.Graph()
  4.  
  5. tf.Session(graph=my_graph)sessとして:
  6.  
  7. x = tf.定数([1,3,6])
  8.  
  9. y = tf.定数([1,1,1])
  10.  
  11. op = tf.add (x,y)

操作を実行するには、tf.Session.run() メソッドを使用する必要があります。このメソッドは、各 Operation オブジェクトを実行するために必要なグラフを実行し、パラメータ fetches で渡された各 Tensor の値を計算することにより、TensorFlow 計算の「ステップ」を実行します。

  1. テンソルフローをtfとしてインポートする
  2.  
  3. my_graph = tf.Graph()
  4.  
  5. tf.Session(graph=my_graph)sessとして:
  6.  
  7. x = tf.定数([1,3,6])
  8.  
  9. y = tf.定数([1,1,1])
  10.  
  11. op = tf.add (x,y)
  12.  
  13. 結果 = sess.run(フェッチ = op)
  14.  
  15. 印刷(結果)
  16.  
  17. >>> [2 4 7]

予測モデル

TensorFlow の仕組みがわかったので、予測モデルを作成する方法を知る必要があります。要するに

機械学習アルゴリズム + データ = 予測モデル

モデルを構築するプロセスは次のとおりです。

(予測モデルを構築するプロセス)

ご覧のとおり、モデルはデータで「トレーニング」された機械学習アルゴリズムで構成されています。モデルが完成すると、次のようなものが得られます。

(予測ワークフロー)

作成したモデルの目的はテキストを分類することです。以下を定義しました。

入力: テキスト、結果: カテゴリ

ラベル付きテキストを使用してトレーニングされたデータセットがあります (各テキストには、それがどのカテゴリに属する​​かを示すラベルがあります)。機械学習では、このタイプのタスクは教師あり学習と呼ばれます。

「私たちは正しい答えを知っています。アルゴリズムはトレーニングデータを反復処理し、教師によって修正されます。

— ジェイソン・ブラウンリー

データをクラスに分類するので、これも分類タスクです。

このモデルを作成するには、ニューラル ネットワークを使用します。

ニューラルネットワーク

ニューラル ネットワークは計算モデル (機械言語と数学的概念を使用してシステムを記述する方法) です。これらのシステムは、明示的にプログラムされるのではなく、自律的に学習し、トレーニングされます。

ニューラル ネットワークも私たちの中枢神経系からヒントを得ています。そこには私たちの神経に似た接続ノードがあります。

パーセプトロンは最初のニューラル ネットワーク アルゴリズムでした。この記事は、パーセプトロンの内部の仕組みをわかりやすく説明しています (「人工ニューロンの内部」のアニメーションは素晴らしいです)。

ニューラル ネットワークの仕組みを理解するために、TensorFlow を使用してニューラル ネットワーク アーキテクチャを構築します。この例では、このアーキテクチャは Aymeric Damien によって使用されました。

ニューラルネットワークアーキテクチャ

ニューラル ネットワークには 2 つの隠し層があります (アーキテクチャ設計の一部として、ネットワークに含める隠し層の数を選択できます)。各隠れ層の役割は、入力を出力層が使用できるものに変換することです。

隠しレイヤー1

(入力層と最初の隠れ層)

最初の隠し層に含まれるノードの数も定義する必要があります。これらのノードは特徴またはニューロンとも呼ばれ、上記の例では各円を使用してノードを表しています。

入力層の各ノードはデータセット内の単語に対応しています(これがどのように機能するかは後で説明します)

ここで説明したように、各ノード (ニューロン) に重みが掛けられます。各ノードには重み値があり、トレーニングフェーズではニューラルネットワークがこれらの値を調整して正しい出力を生成します(これについては後で詳しく説明します)

入力のない重みを乗算することに加えて、ネットワークはエラーも追加します (ニューラル ネットワークにおけるエラーの役割)。

アーキテクチャでは、入力に重みを掛けてその値をバイアスに追加し、このデータもアクティベーション関数に渡されます。この活性化関数は、各ノードの最終出力を定義します。たとえば、各ノードがライトであり、アクティベーション関数によってライトが点灯するかどうかが決定されるとします。

活性化関数には多くの種類があります。 Rectified Linear Unit (ReLu) を使用します。この関数は次のように定義されます:

f(x) = max(0,x) [xと0(ゼロ)の間の最大の数値を出力する]

たとえば、x = -1 の場合、f(x) = 0 (ゼロ)、x = 0.7 の場合、f(x) = 0.7 です。

隠しレイヤー2

2 番目の隠し層は最初の隠し層とまったく同じことを行いますが、2 番目の層への入力は最初の層の出力になります。

(*** および 2 番目の隠し層)

出力層

さて、いよいよ最後の層である出力層に到達します。このレイヤーの結果を取得するには、One-Hot エンコーディングを使用します。このエンコーディングでは、1 ビットのみが値 1 を持ち、他のすべてのビットは値 0 を持ちます。たとえば、3 つのカテゴリ (スポーツ、宇宙、コンピュータ グラフィックス) をエンコードする場合は、次のようになります。

したがって、出力ノードの数は、入力データセットのクラスの数になります。

出力層の値にも重みが掛けられ、誤差も加算されますが、今度は活性化関数が異なります。

各テキストにカテゴリのラベルを付けますが、これらのカテゴリは互いに独立しています (テキストは同時に 2 つのカテゴリに属する​​ことはできません)。これを念頭に置いて、ReLu 活性化関数の代わりに Softmax 関数を使用します。この関数は、各完全な出力を 0 から 1 の間の値に変換し、すべての単位の合計が 1 になるようにします。この方法では、出力によって各カテゴリ内の各テキストの確率がわかります。

  1. | 1.2 0.46|
  2.  
  3. | 0.9 -> [ソフトマックス] -> 0.34|
  4.  
  5. | 0.4 0.20|

これで、ニューラル ネットワークのデータ フロー グラフができました。これまで見てきた内容をコードに変換すると、結果は次のようになります。

  1. # ネットワークパラメータ
  2.  
  3. n_hidden_​​1 = 10 # 第 1 層の特徴
  4.  
  5. n_hidden_​​2 = 5 # 第2層の特徴
  6.  
  7. n_input = total_words #語彙内の単語数
  8.  
  9. n_classes = 3 # カテゴリ: グラフィックス、スペース 野球
  10.  
  11. def 多層パーセプトロン(入力テンソル、重み、バイアス):
  12.  
  13. レイヤー1の乗算 = tf.matmul(入力テンソル、重み[ 'h1' ])
  14.  
  15. レイヤー1の追加 = tf.add (レイヤー1の乗算、バイアス[ 'b1' ])
  16.  
  17. レイヤー1のアクティベーション = tf.nn.relu(レイヤー1の追加)
  18.  
  19. # RELU 活性化による隠れ層
  20.  
  21. レイヤー2の乗算 = tf.matmul(レイヤー1の活性化、重み[ 'h2' ])
  22.  
  23. レイヤー2の加算 = tf.add (レイヤー2の乗算、バイアス[ 'b2' ])
  24.  
  25. レイヤー2のアクティベーション = tf.nn.relu(レイヤー2の追加)
  26.  
  27. #線形活性化出力
  28.  
  29. out_layer_multiplication = tf.matmul(layer_2_activation, 重み[ 'out' ])
  30.  
  31. out_layer_addition = out_layer_multiplication + バイアス[ 'out' ]戻り値out_layer_addition

(出力層の活性化関数については後ほど説明します)

ニューラルネットワークはどのように学習するのでしょうか?

先ほど見たように、ニューラル ネットワークがトレーニングされると、重みの値が更新されます。ここで、TensorFlow のコンテキストでこれがどのように起こるかを見てみましょう。

tf.変数

重みと誤差は変数 (tf.Variable) に保存されます。これらの変数は、run() の呼び出し全体にわたってグラフ内に保持されます。機械学習では通常、正規分布を通じて重みとバイアスの値を決定します。

  1. 重み = {
  2.  
  3. 'h1' : tf.Variable(tf.random_normal([n_input, n_hidden_​​1])),
  4.  
  5. 'h2' : tf.Variable(tf.random_normal([n_hidden_​​1, n_hidden_​​2])),
  6.  
  7. '出力' : tf.Variable(tf.random_normal([n_hidden_​​2, n_classes]))
  8.  
  9. }
  10.  
  11. バイアス = {
  12.  
  13. 'b1' : tf.Variable(tf.random_normal([n_hidden_​​1])),
  14.  
  15. 'b2' : tf.Variable(tf.random_normal([n_hidden_​​2]))、
  16.  
  17. '出力' : tf.Variable(tf.random_normal([n_classes]))
  18.  
  19. }

ニューラル ネットワークを初めて実行するとき (つまり、重みが正規分布によって定義されるとき) は次のようになります。

  1. 入力:x
  2.  
  3. 重み: w
  4.  
  5. バイアス: b
  6.  
  7. 出力 : z
  8.  
  9. 期待:期待値

ネットワークが学習しているかどうかを知るには、出力値 (Z) と期待値 (expected) を比較する必要があります。この差(損失)をどのように計算するのでしょうか?この問題を解決する方法はたくさんあります。分類タスクを実行しているため、損失を測定する最良の方法はクロスエントロピー誤差です。

James D. McCaffrey 氏は、なぜこれがこの種のタスクに最適なアプローチなのかについて優れた説明を書いています。

TensorFlow では、tf.nn.softmax_cross_entropy_with_logits() メソッドを使用してクロスエントロピー誤差 (これはソフトマックス活性化関数です) を計算し、平均誤差 (tf.reduced_mean()) を計算します。

  1. # モデルの構築
  2.  
  3. 予測 = 多層パーセプトロン(入力テンソル、重み、バイアス)
  4.  
  5. # 損失を定義する
  6.  
  7. entropy_loss = tf.nn.softmax_cross_entropy_with_logits(logits=予測、ラベル=出力テンソル)
  8.  
  9. 損失 = tf.reduce_mean(エントロピー損失)

出力エラー(取得した実際の値と正しい値の差)を最小限に抑えるために、重みとエラーの最適な値を渡す必要があります。これを実行するには、勾配降下法を使用します。より具体的には、確率的勾配降下法を使用する必要があります。

(勾配降下法。出典: https://sebastianraschka.com/faq/docs/closed-form-vs-gd.html)

勾配降下法を計算するには、Adaptive Moment Estimation (Adam) を使用します。 TensorFlow でこのアルゴリズムを使用するには、最適な重み値を見つけるための値の増分ステップ サイズを決定する learning_rate 値を渡す必要があります。

メソッド tf.train.AdamOptimizer(learning_rate).minimize(loss) は、次の 2 つのことを実行する構文糖です。

  1. compute_gradients(loss, <変数のリスト>)
  2. apply_gradients(<変数のリスト>)

このメソッドはすべての tf.Variables を新しい値で更新するため、変数のリストを渡す必要はありません。これで、ネットワークをトレーニングするためのコードができました。

  1. 学習率 = 0.001
  2.  
  3. # モデルの構築
  4.  
  5. 予測 = 多層パーセプトロン(入力テンソル、重み、バイアス)
  6.  
  7. # 損失を定義する
  8.  
  9. entropy_loss = tf.nn.softmax_cross_entropy_with_logits(logits=予測、ラベル=出力テンソル)
  10.  
  11. 損失 = tf.reduce_mean(エントロピー損失)
  12.  
  13. オプティマイザー = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(損失)

データ操作

使用するデータセットには英語のテキストが大量に含まれているため、このデータを操作してニューラル ネットワークに渡す必要があります。これを実現するには、次の 2 つのことが必要です。

  1. 各ジョブのインデックスを作成する
  2. 各テキストについて、単語がテキスト内にある場合は値が1、そうでない場合は値が0となる行列を作成します。

このプロセスを理解するためにコードを見てみましょう。

  1. import numpy as np #numpy科学計算用のパッケージです
  2.  
  3. コレクションからカウンターをインポート
  4.  
  5. 語彙 = カウンター()
  6.  
  7. text = "Hi from Brazil" #text.split( ' ' )内の単語をすべて取得:
  8.  
  9. 語彙[単語]+=1
  10.  
  11. #単語インデックス変換する
  12.  
  13. get_word_2_index(語彙):
  14.  
  15. word2index = {} for i,word in enumerate(vocab):
  16.  
  17. word2index[単語] = i
  18.  
  19. 単語2インデックスを返す
  20.  
  21. #これでインデックスができました 
  22.  
  23. word2index = get_word_2_index(語彙)
  24.  
  25. 総語数 = len(語彙数)
  26.  
  27. #これNumPy配列(行列)を作成する方法です
  28.  
  29. 行列 = np.zeros((total_words), dtype= float )
  30.  
  31. #次に、 text.split()word の値を入力します。
  32.  
  33. 行列[単語2インデックス[単語]] += 1print(行列)
  34.  
  35. >>> [ 1. 1. 1.]

上記の例では、テキストは「Hi from Brazil」で、マトリックスは[1.1.1.]です。テキストが「Hi」だけだったらどうなるでしょうか?

  1. 行列 = np.zeros((total_words), dtype= float )
  2.  
  3. text = "Hi"、 text.split ()内の単語:
  4.  
  5. 行列[単語2インデックス[単語.lower ()]] += 1print(行列)
  6.  
  7. >>> [ 1. 0. 0. ]

これはラベル (テキストの分類) と同じになりますが、ワンホット エンコーディングを使用します。

  1. y = np.zeros((3), dtype= float )カテゴリ == 0の場合:
  2.  
  3. y[0] = 1. # [ 1. 0. 0.]
  4.  
  5. elif カテゴリ == 1:
  6.  
  7. y[1] = 1. # [ 0. 1. 0.]そうでない場合:
  8.  
  9. y[2] = 1. # [ 0. 0. 1.]

グラフを実行して結果を取得する

ここで、最も重要な部分、つまりモデルから結果を取得する作業が始まります。まず、入力データセットを詳しく見てみましょう。

データセット

約 20 のトピックを含む 18,000 件の投稿のデータセットの場合、20 のニュースグループが使用されます。これらのデータセットをロードするには、scikit-learn ライブラリを使用します。使用しているカテゴリは、comp.graphics、sci.space、rec.sport.baseball の 3 つだけです。 scikit-learn には、トレーニング用とテスト用の 2 つのサブセットがあります。モデルを作成するときに選択に影響する可能性があるため、テスト データを確認しないことをお勧めします。一般化に適したモデルを作成する必要があるため、この特定のテスト データを予測するモデルを作成する必要はありません。

データセットをロードする方法のコードは次のとおりです。

  1. sklearn.datasetsからfetch_20newsgroups をインポートします
  2.  
  3. カテゴリ = [ "comp.graphics" "sci.space" "rec.sport.baseball" ]
  4.  
  5. newsgroups_train = fetch_20newsgroups(サブセット= 'train' 、categories=カテゴリ)
  6.  
  7. newsgroups_test = fetch_20newsgroups(サブセット= 'test' 、categories=カテゴリ

モデルのトレーニング

ニューラル ネットワークの用語では、エポック = 前方パス (出力値の取得) とすべてのトレーニング例を通る後方パス (重みの更新) です。

tf.Session.run() メソッドを覚えていますか? 詳しく見てみましょう:

  1. tf.Session.run(フェッチ、feed_dict=なし、options=なし、run_metadata=なし)

この記事の冒頭のデータフロー図では、 and 演算子を使用しましたが、実行する項目のリストを渡すこともできます。このニューラル ネットワークの実行中には、損失の計算と最適化のステップという 2 つのことが行われます。

feed_dict パラメータは、各実行にフィードするデータです。このデータを渡すには、tf.placeholders(feed_dictに提供)を定義する必要があります。

TensorFlow のドキュメントに記載されているとおりです。

「プレースホルダーは入力先としてのみ存在します。初期化する必要はなく、データも含まれません。」— 出典

したがって、プレースホルダーは次のように定義されます。

  1. n_input = total_words #語彙内の単語数
  2.  
  3. n_classes = 3 # カテゴリ: グラフィックス、科学、宇宙 野球
  4.  
  5. input_tensor = tf.placeholder(tf.float32,[None, n_input], name = "input" )
  6.  
  7. output_tensor = tf.placeholder(tf.float32,[None, n_classes], name = "output" )

トレーニング データをバッチで分離することも必要です。

「入力目的でプレースホルダーを使用する場合、 tf.placeholder(…, shape=[None, …]) を使用してプレースホルダーを作成することで、可変バッチディメンションを指定できます。shape の None 要素は、サイズ変更可能なディメンションに対応します。」 — 出典

モデルをテストするときは、辞書に大きなバッチを入力するため、可変バッチ ディメンションを定義する必要があります。

get_batches() 関数はバッチ サイズ内のテキストの数を返します。これでモデルを実行できます。

  1. training_epochs = 10 # グラフを起動する
  2.  
  3. tf.Session()を sessとして使用:
  4.  
  5. sess.run(init) # 変数を初期化します (正規分布を覚えていますか?)
  6.  
  7. # 範囲内のエポックトレーニング サイクル(training_epochs):
  8.  
  9. 平均コスト = 0。
  10.  
  11. total_batch = int (len(newsgroups_train.data)/batch_size)
  12.  
  13. #すべてのバッチをループしますfor i in range(total_batch):
  14.  
  15. batch_x、batch_y = get_batch(newsgroups_train、i、batch_size)
  16.  
  17. # 最適化オペレーション(バックプロパゲーション)コストオペレーション(損失値を取得するため)を実行します
  18.  
  19. c,_ = sess.run([損失、オプティマイザー]、feed_dict={入力テンソル: batch_x、出力テンソル: batch_y})

これで、トレーニング済みのモデルができました。テストするには、ダイアグラム要素も作成する必要があります。モデルの精度を測定するため、予測値のインデックスと正しい値のインデックス(ワンホットエンコーディングを使用しているため)を取得し、それらが等しいかどうかを確認し、すべてのテストデータセットの平均を計算する必要があります。

  1. # テストモデル
  2.  
  3. index_prediction = tf.argmax(予測, 1)
  4.  
  5. インデックス修正 = tf.argmax(出力テンソル、1)
  6.  
  7. 正しい予測 = tf.equal(インデックス予測、インデックス正しい)
  8.  
  9. # 精度を計算する
  10.  
  11. 精度 = tf.reduce_mean( tf.cast (correct_prediction, "float" ))
  12.  
  13. 合計テストデータ = len(newsgroups_test.target)
  14.  
  15. batch_x_test、batch_y_test = get_batch(newsgroups_test、0、total_test_data) です。
  16.  
  17. print( "精度: " , 精度.eval({input_tensor: batch_x_test, output_tensor: batch_y_test}))
  18.  
  19. エポック: 0001 損失 = 1133.908114347
  20.  
  21. エポック: 0002 損失 = 329.093700409
  22.  
  23. エポック: 0003 損失= 111.876660109
  24.  
  25. エポック: 0004 損失= 72.552971845
  26.  
  27. エポック: 0005 損失= 16.673050320
  28.  
  29. エポック: 0006 損失= 16.481995190
  30.  
  31. エポック: 0007 損失 = 4.848220565
  32.  
  33. エポック: 0008 損失 = 0.759822878
  34.  
  35. エポック: 0009 損失 = 0.000000000
  36.  
  37. エポック: 0010 損失 = 0.079848485
  38.  
  39. 最適化が完了しました!
  40.  
  41. 精度: 0.75

これで完了です。ニューラル ネットワークを使用してテキストをさまざまなカテゴリに分類するモデルを作成しました。おめでとう!

最終的なコードを含むノートブックはここから見ることができます。

ヒント: 定義した値を変更して、その変更がトレーニング時間とモデルの精度にどのように影響するかを確認します。

他にご質問やご提案がございましたら、コメントを残してください。読んでくれてありがとう!

<<:  決定木からランダムフォレストへ: ツリーベースアルゴリズムの原理と実装

>>:  マッキンゼーのレポート:これらの業界が人工知能に転換しなければ、ますます取り残されることになる

ブログ    
ブログ    
ブログ    

推薦する

2022年のスマート製造のトレンド

製造業は過去 1 世紀にわたって大きく変化しました。 新しい高度なテクノロジーが業界を前進させるにつ...

...

...

WOT2019 検索推奨アルゴリズムフォーラム: さまざまな分野における AI ベースの検索推奨の実用化

6月21日、WOT2019グローバルテクノロジーサミットとグローバル人工知能テクノロジーサミットが北...

Apple の生成 AI ツール Apple GPT: 遅れて登場したが、他の利点もある

Appleは、ChatGPTやGoogleのBardのような大規模言語モデル(LLM)と競合する独自...

人工知能は大学のキャンパスにどのような変化をもたらしたのでしょうか?

[[279290]] [51CTO.com クイック翻訳] 大学はどのようにして、個人の教育キャリ...

...

...

...

サイバーセキュリティにおける人工知能の長所と短所を探る

急速に進化するデジタル環境において、人工知能 (AI) とサイバーセキュリティの組み合わせは、進化す...

...

OpenAI と Mistral AI によって人気を博した MoE の背景にあるストーリーとは?ハイブリッドアーキテクチャの導入に関する包括的なガイド

専門家の混合 (MoE) は、LLM の効率性と精度を向上させるためによく使用される手法です。このア...

清華大学が世界初のオンチップ学習メモリスタメモリコンピューティング統合チップを開発、その成果がサイエンス誌に掲載された。

10月9日、清華大学の公式Weiboアカウントは、オンチップ学習をサポートする世界初のメモリスタス...

利便性を超えて:スマートホームは信頼できるのか?

映画鑑賞の夜に快適なアームチェアに腰を下ろすと、プロジェクターが起動し、スマートライトが自動的に暗く...