Compute Engine で機械学習を使用して商品のおすすめを生成する

Last reviewed 2016-02-26 UTC

Google Cloud を使用すると、オンライン ストアでユーザーに関連商品をおすすめ(レコメンデーション)するための、スケーラビリティが高く、効率的かつ効果的なサービスを構築できます。

現在、オンライン販売サイトでの競争はかつてないほど激化しています。顧客のオンラインでの購入金額は全体では増えているものの、小売店あたりの売り上げ額は減っています。カートあたりの平均数量が減っているのは、競争がワンクリック購入になっていることに原因の一端があります。関連商品のレコメンデーションを潜在顧客に提供することは、買い物客を大口顧客に変え、平均注文数量を増加させることに大いに役立ちます。

このソリューションを読み終えると、基本的なレコメンデーション エンジンをサポートする環境を設定し、特定の作業負荷のニーズに基づいてエンジンの拡張や改善を行うことができます。Google Cloud でレコメンデーション エンジンを実行することで、実行するソリューションに柔軟性とスケーラビリティがもたらされます。

このソリューションでは、不動産賃貸会社が、関連するレコメンデーションを計算し、ウェブサイトを閲覧している顧客にそれを提示する方法について説明します。

シナリオ

鈴木さんは、休暇のために借りる家を探しています。貸別荘のウェブサイトにプロフィールを登録していて、以前にも借りたことがあり、いくつかのツアーを評価したことがあります。彼女は自分の好みや嗜好に基づいたレコメンデーションを期待しています。システムは鈴木さんの嗜好をすでに把握しているはずです。彼女の評価ページに基づくと、彼女は house タイプの宿泊施設を好むことは明白です。システムは、同じようなタイプのものをおすすめする必要があります。

休暇用の物件賃貸のユーザー インターフェース

ソリューションの概要

顧客がリアルタイムで閲覧する場合、または後からメールを介して閲覧する場合でも、レコメンデーションを提供するには、複数の行動を発生させる必要があります。最初は、顧客の嗜好や好みに関する情報がほとんどないため、アイテムの属性に関するレコメンデーションのみを基本として使用します。しかし、ユーザーの嗜好や好みに関するデータを収集して、システムがユーザーについて学習できるようにする必要があります。時間の経過とともに十分なデータが収集できたら、機械学習アルゴリズムを使用して、有用な分析を行ったり有意義なレコメンデーションを提供できるようになります。システムを定期的に再トレーニングできるようにすることで、他のユーザーの入力でも結果を改善することができます。このソリューションでは、機械学習アルゴリズムの恩恵を受けるのに十分なデータがすでにあるレコメンデーション システムを使用します。

レコメンデーション エンジンは、一般的に次の 4 つのフェーズでデータを処理します。

収集、保存、分析、レコメンデーションの各フェーズ

このようなシステムのアーキテクチャは、次の図で表すことができます。

フロントエンド、ストレージ、機械学習によるアーキテクチャ

各手順は、要件を満たすためにカスタマイズできます。システムは次のもので構成されています。

  1. データを収集するためにユーザーとの接点を記録するスケーラブルなフロントエンド。

  2. 機械学習プラットフォームがアクセスできる永続ストレージ。このストレージにデータをロードすることで、データのインポート、エクスポート、変換などのいくつかの手順を含めることができます。

  3. 関連するレコメンデーションを作成するため、既存のコンテンツを分析することができる機械学習プラットフォーム。

  4. レコメンデーションの適時性要件に基づいて、リアルタイムでまたは後から、フロントエンドで使用できるストレージ。

コンポーネントの選択

スピード、シンプル、コスト管理、精度のちょうどよい妥協点を見つけるために、このソリューションでは App EngineCloud SQL を使用し、さらに Dataproc を使用して App Engine 上で Apache Spark を実行します。

App Engine は、管理をほとんど必要とせずに、1 秒間で数万のクエリを処理できます。ウェブサイトを作成する場合でも、データをバックエンドのストレージに保存する場合でも、App Engine を使用すると、コードを記述して、ほんの数秒で本番環境にデプロイすることができます。

Cloud SQL もシンプルなデプロイを提供しています。Cloud SQL は、最大 32 コアの仮想マシンと、最大 208 GB の RAM まで拡張でき、ストレージはオンデマンドで 10 TB(30 IOPS/GB と数千の同時接続)まで増やすことができます。これらの仕様は、このソリューションのサンプルや、現実世界の多くのレコメンデーション エンジンには十分です。Cloud SQL も、Spark から直接アクセスできるという利点があります。

Spark は、一般的な Hadoop セットアップよりもはるかに優れたパフォーマンス(10~100 倍高速)を提供しています。Spark MLlib を使用すると、何億もの評価を数分で分析できるため、レコメンデーションのアジリティが向上し、管理者はアルゴリズムをより頻繁に実行できるようになります。Spark は、できるだけ多くのメモリを計算に利用して、ディスクへのラウンド トリップを減らします。また、I/O を最小限にすることも試みます。このソリューションでは、分析インフラストラクチャをホストするために Compute Engine を使用しています。Compute Engine は、秒単位のオンデマンドの料金体系を通じて、分析の料金をできる限り低く抑えることに役立ちます。

次の図は、前のアーキテクチャ図にマップしたものですが、各手順で使用されるテクノロジーを示しています。

App Engine、Cloud SQL、Spark、Compute Engine を使用するアーキテクチャ

データの収集

レコメンデーション エンジンは、ユーザーの暗黙の行動や明示的な入力に基づいて、ユーザーに関するデータを収集できます。

ユーザーの活動のログを保持しているため、行動データは容易に収集できます。すでにアプリケーションを使用しているユーザーからの追加のアクションを必要としないため、このデータの収集も簡単です。この方法の欠点は、分析が困難であるということです。たとえば、関心度の低いログから関心度の高いログをフィルタリングするのは、手間がかかる場合があります。

レビューを書き込むなどユーザーの追加アクションが必要となるため、入力データの収集が困難になる場合があります。さまざまな理由から、ユーザーがこのデータを提供したくない場合があります。しかし、ユーザーの好みを理解するという点では、これらのデータを使用すると、結果がかなり正確なものになります。

データの保存

アルゴリズムに利用できるデータが多くなるほど、レコメンデーションが向上します。これは、任意のレコメンデーション プロジェクトをすばやくビッグデータ プロジェクトに変えられることを意味します。

レコメンデーションを作成するために使用するデータのタイプは、使用するストレージのタイプを決定するのに役立ちます。NoSQL データベース、標準の SQL データベース、またはなんらかのオブジェクト ストレージも使用することができます。これらのオプションはそれぞれ、ユーザーの入力や行動をキャプチャしているかどうか、実装のしやすさ、ストレージが管理できるデータの量、環境の他の部分との統合、移植性などの要因に応じて選択できます。

ユーザーの評価やイベントを保存する際に、スケーラブルなマネージド データベースが、必要な運用タスクの量を最小限にして、レコメンデーションに集中できるようにします。Cloud SQL は、これら両方のニーズを満たし、また、Spark からのデータの直接読み込みを容易にします。

次のサンプルコードは、Cloud SQL テーブルのスキーマを示しています。Accommodation テーブルは賃貸物件を表し、Rating テーブルは特定の物件に対するユーザーの評価を表しています。

CREATE TABLE Accommodation
(
  id varchar(255),
  title varchar(255),
  location varchar(255),
  price int,
  rooms int,
  rating float,
  type varchar(255),
  PRIMARY KEY (ID)
);

CREATE TABLE Rating
(
  userId varchar(255),
  accoId varchar(255),
  rating int,
  PRIMARY KEY(accoId, userId),
  FOREIGN KEY (accoId)
    REFERENCES Accommodation(id)
);

Spark は、Hadoop HDFS や Cloud Storage などのさまざまなソースからデータを受け取ることができます。このソリューションでは、Spark の Java Database Connectivity(JDBC)コネクタを使用して、Cloud SQL から直接データを受け取ります。Spark ジョブは並列で実行されるため、すべてのクラスタ インスタンスでこのコネクタを使用可能にする必要があります。

データの分析

分析フェーズを設計するには、アプリケーションの要件を理解する必要があります。これらの要件には次のものがあります。

  • レコメンデーションの適時性。どのくらい迅速にアプリケーションがレコメンデーションを提示する必要があるか。

  • データのフィルタリング方法。アプリケーションがユーザーの嗜好のみに基づいてレコメンデーションを提示するのか、他のユーザーが考えていることに基づいてデータをピボットするのか、または論理的に適合する商品も一緒にピボットするのか。

適時性を理解する

データを分析する際に最初に考慮すべき要因は、どのくらい迅速にユーザーにレコメンデーションを提示する必要があるのかということです。レコメンデーションをすぐに提示する(ユーザーが商品を閲覧している時など)場合は、たとえば後日、顧客にレコメンデーションを含むメールを送信する場合に比べて、よりすばやいタイプの分析が必要になります。

  • リアルタイム システムは、作成時にデータを処理できます。このタイプのシステムには、通常、イベントのストリームを処理して分析できるツールが含まれています。瞬時にレコメンデーションを提示するためには、リアルタイム システムが必要になります。

  • バッチ分析では、定期的にデータを処理する必要があります。このアプローチは、分析を関連付けるためには、毎日の販売量など、十分なデータが作成される必要があることを意味します。バッチシステムは、後日メールを送信する場合にはうまく機能します。

  • ほぼリアルタイムの分析では、データをすばやく収集して、数分または数秒ごとに分析を更新できるようにします。ほぼリアルタイム システムは、同じブラウジング セッション中にレコメンデーションを提供するのに適しています。

レコメンデーションは、これら 3 つの適時性カテゴリのいずれにも該当する場合がありますが、オンライン販売ツールの場合は、トラフィックの量とアプリケーションが受け取るユーザーの入力の量によっては、ほぼリアルタイムとバッチの中間を検討することができます。分析を実行しているプラットフォームは、データが保存されているデータベースから機能することも、定期的に永続ストレージに保存されるダンプ上で機能することもできます。

データのフィルタリング

レコメンデーション エンジンを構築するコア コンポーネントがフィルタリングです。最も一般的な方法には次のものがあります。

  • コンテンツ ベース: ユーザーが閲覧しているものまたは好むものと同様の属性を持つ人気のあるおすすめ商品。

  • クラスタ: 他のユーザーの行動とは関係なく、一緒に購入するとよいおすすめ商品。

  • 協調: ユーザーが閲覧しているまたは好む商品と同じものを好む他のユーザーが、気に入ったおすすめ商品。

Google Cloud は、これらすべての方法をサポートできますが、このソリューションでは、Apache Spark を使用して実装されている協調フィルタリングを主に取り上げています。コンテンツ ベースのフィルタリングまたはクラスタ フィルタリングの詳細については、付録をご覧ください。

協調フィルタリングを使用すると、商品属性を抽象化して、ユーザーの嗜好に基づいて予測を行うことができます。このフィルタリングの出力は、過去に同じ商品を気に入った 2 人の異なるユーザーが、今度も同じものを気に入るだろうという仮定に基づいています。

評価や接点に関するデータを、商品とユーザーを次元とする一組のマトリックスとして表すことができます。協調フィルタリングは、特定のユーザーと商品のペアに対し、マトリックス内で不足しているセルを予測しようとします。次の 2 つのマトリックスは似ていますが、2 番目のマトリックスは、1 番目のマトリックスの既存の評価を数字 1 に置き換えて、不足している評価を数字 0 に置き換えることで推定したものです。結果のマトリックスは真理値表で、数字 0 はユーザーによる商品との接点を表しています。

評価マトリックス 接点マトリックス
評価マトリックス 評価マトリックス

協調フィルタリングを使用するための次の 2 つの異なる方法があります。

  • メモリベースのフィルタリングは、商品やユーザーの類似度を計算します。

  • モデルベースのフィルタリングは、ユーザーがアイテムを評価またはアイテムとやり取りする方法を指示する根底にあるパターンを学習しようとします。

このソリューションでは、ユーザーがアイテムをすでに評価している、モデルベースのアプローチを使用しています。

このソリューションで必要なすべての分析機能は、Spark プログラミング言語に Python インターフェースを提供する PySpark を介して利用できます。Scala または Java を使用して利用できるその他のオプションについては、Spark のドキュメントをご覧ください。

モデルのトレーニング

Spark MLlib は、Alternating Least Squares(ALS)アルゴリズムを実装してモデルをトレーニングします。次のパラメータのさまざまな組み合わせを使用して、バリアンスとバイアスの最良の妥協点を得ます。

  • ランク: ユーザーが評価を与えるように導いた未知の要素の数。これには、年齢、性別、場所などの要素が含まれます。ランクが多いほど、レコメンデーションはある程度まで向上します。メモリと CPU が許せば、5 で開始してレコメンデーションの改善率が下降を示すまで 5 ずつ増やしていくのが良いアプローチです。

  • ラムダ: 高いバリアンスと低いバイアスによって表される過剰適合を防止するための正則化パラメータ。バリアンスは、複数回実行して、特定の時点での予測が、その時点の理論的に正しい値と比較して、どのくらい変動するかを表します。バイアスは、生成された予測が、予測しようとしている実際の値からどのくらい離れているかを表します。過剰適合は、モデルが既知のノイズを使用したトレーニング データではうまく機能したものの、実際のテストデータでうまく機能しない場合に発生します。ラムダが大きいほど、過剰適合は少なくなりますが、バイアスは大きくなります。テストには、値 0.01、1、10 が適しています。

    次の図は、バリアンスとバイアスとの関係を示しています。ブルズアイは、アルゴリズムが予測しようとしている値を表しています。

    バリアンスとバイアス(左上がベスト)
    バリアンスとバイアス
  • イテレーション: トレーニングを実行する回数。この例では、ランクとラムダのさまざまな組み合わせに対して、5、10、20 回のイテレーションを行います。

次のサンプルコードは、Spark で ALS モデルのトレーニングを開始する方法を示しています。

from pyspark.mllib.recommendation import ALS
model = ALS.train(training, rank = 10, iterations = 5, lambda_=0.01)

適切なモデルを見つける

ALS アルゴリズムを使用する協調フィルタリングは、次の 3 つの異なるデータセットに基づいています。

  • トレーニング セット: 既知の出力によるデータが含まれています。このセットは、完璧な結果がどのようになるかを示します。このソリューションでは、ユーザーの評価が含まれています。

  • 検証セット: トレーニングを調整してパラメータの適切な組み合わせを選択し、最適なモデルを選択するために役立つデータが含まれています。

  • テストセット: 最適なトレーニング モデルのパフォーマンスを評価するために使用されるデータが含まれています。これは、現実世界の例で分析を実行する場合と同じものになります。

最適なモデルを見つけるには、計算されたモデル、検証セット、およびそのサイズに基づいて二乗平均平方根(RMSE)を計算する必要があります。RMSE が小さいほど良いモデルとなります。

レコメンデーションの提供

ユーザーが結果を迅速かつ容易に利用できるようにするには、結果をオンデマンドで照会できるデータベースにロードする必要があります。ここでも、Cloud SQL が優れた選択肢となります。Spark 1.4 以降では、予測の結果を PySpark から直接データベースに書き込むことができます。

Recommendation テーブルのスキーマは次のようになります。

CREATE TABLE Recommendation
(
  userId varchar(255),
  accoId varchar(255),
  prediction float,
  PRIMARY KEY(userId, accoId),
  FOREIGN KEY (accoId)
    REFERENCES Accommodation(id)
);

コードのチュートリアル

このセクションでは、モデルをトレーニングするためのチュートリアルを提供します。

Cloud SQL からデータを取得する

Spark SQL コンテキストを使用すると、JDBC コネクタを介して簡単に Cloud SQL インスタンスに接続できます。読み込まれたデータは、DataFrame 形式になります。

jdbcUrl    = 'jdbc:mysql://%s:3306/%s?user=%s&password=%s' % (CLOUDSQL_INSTANCE_IP, CLOUDSQL_DB_NAME, CLOUDSQL_USER, CLOUDSQL_PWD)
dfAccos = sqlContext.read.jdbc(url=jdbcUrl, table=TABLE_ITEMS)
dfRates = sqlContext.read.jdbc(url=jdbcUrl, table=TABLE_RATINGS)

DataFrame を RDD に変換し、各種データセットを作成する

Spark は、要素の並列処理を円滑化する 耐障害性分散データセット(RDD)と呼ばれるコンセプトを使用します。RDD は、永続ストレージから作成された読み取り専用のコレクションです。これらはメモリ内で処理することができるため、反復処理に適しています。

予測を行うための最適なモデルを得るには、データセットを 3 つの異なるセットに分割する必要があることを思い出してください。次のコードは、重複しない値を 60、20、20 の割合でランダムに分割するヘルパー関数を使用しています。

rddTraining, rddValidating, rddTesting = dfRates.rdd.randomSplit([6,2,2])

各種パラメータに基づいてモデルをトレーニングする

ALS メソッドを使用する場合は、システムがランク、正則化、イテレーションの各パラメータを使用して、最適なモデルを見つける必要があることを思い出してください。評価が存在するので、train 関数の結果を、検証セットと比較する必要があります。ユーザーの嗜好もトレーニング セットに含まれていることを確認します。

for cRank, cRegul, cIter in itertools.product(ranks, reguls, iters):

  model = ALS.train(rddTraining, cRank, cIter, float(cRegul))
  dist = howFarAreWe(model, rddValidating, nbValidating)
  if dist < finalDist:
    print("Best so far:%f" % dist)
    finalModel = model
    finalRank  = cRank
    finalRegul = cRegul
    finalIter  = cIter
    finalDist  = dist
def howFarAreWe(model, against, sizeAgainst):
  # Ignore the rating column
  againstNoRatings = against.map(lambda x: (int(x[0]), int(x[1])) )

  # Keep the rating to compare against
  againstWiRatings = against.map(lambda x: ((int(x[0]),int(x[1])), int(x[2])) )

  # Make a prediction and map it for later comparison
  # The map has to be ((user,product), rating) not ((product,user), rating)
  predictions = model.predictAll(againstNoRatings).map(lambda p: ( (p[0],p[1]), p[2]) )

  # Returns the pairs (prediction, rating)
  predictionsAndRatings = predictions.join(againstWiRatings).values()

  # Returns the variance
  return sqrt(predictionsAndRatings.map(lambda s: (s[0] - s[1]) ** 2).reduce(add) / float(sizeAgainst))

ユーザーのための上位予測を計算する

合理的な予測が行えるモデルを入手したので、これを使用して、ユーザーの嗜好と同様の嗜好を持つ他のユーザーによる評価に基づいて、ユーザーが興味を持つ可能性が最も高いものが何かを確認することができます。この手順では、先に説明したマトリックス マッピングを参照できます。

# Build our model with the best found values
# Rating, Rank, Iteration, Regulation
model = ALS.train(rddTraining, BEST_RANK, BEST_ITERATION, BEST_REGULATION)

# Calculate all predictions
predictions = model.predictAll(pairsPotential).map(lambda p: (str(p[0]), str(p[1]), float(p[2])))

# Take the top 5 ones
topPredictions = predictions.takeOrdered(5, key=lambda x: -x[2])
print(topPredictions)

schema = StructType([StructField("userId", StringType(), True), StructField("accoId", StringType(), True), StructField("prediction", FloatType(), True)])

dfToSave = sqlContext.createDataFrame(topPredictions, schema)
dfToSave.write.jdbc(url=jdbcUrl, table=TABLE_RECOMMENDATIONS, mode='overwrite')

上位予測を保存する

すべての予測のリストが作成されたので、システムがユーザーにレコメンデーションをいくつか提供できるように、上位 10 の予測を Cloud SQL に保存できます。たとえば、ユーザーがサイトにログインしたときに、これらの予測を使用できます。

dfToSave = sqlContext.createDataFrame(topPredictions, schema)
dfToSave.write.jdbc(url=jdbcUrl, table=TABLE_RECOMMENDATIONS, mode='overwrite')

ソリューションの実行

このソリューションを実行するには、GitHub ページの手順を行います。この手順で、ユーザーへのレコメンデーションを計算して表示することができます。

最後の SQL コードは、データベースから上位のレコメンデーションをフェッチして、鈴木さんのウェルカム ページに表示します。

このクエリは、Google Cloud コンソールまたは MySQL クライアントで実行すると、次のサンプルのような結果を返します。

5 ユーザーの SQL クエリからの結果

ウェブサイトで同じクエリを使用することで、ウェルカム ページを強化し、訪問者を顧客に転換できる可能性を高めることができます。

ユーザー インターフェースと結果

これは、シナリオ セクションで説明したように、システムが鈴木さんについてすでに把握している情報に基づいて、彼女の好みとかなり類似しているように見えます。

チュートリアル

設定手順とソースコードを含むチュートリアルの完全な内容は、GitHub から入手することができます。

費用

このチュートリアルでは、課金対象である次の Google Cloud コンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。

次のステップ

  • Google Cloud に関するリファレンス アーキテクチャ、図、チュートリアル、ベスト プラクティスを確認する。Cloud Architecture Center を確認する。
  • Google Cloud プロダクトを使用して、エンドツーエンドのソリューションを作成する方法を学習する。
  • モニタリングの詳細については、Dataproc のドキュメントで出力またはウェブ インターフェースをご覧ください。

付録

クロス フィルタリング

効果的かつスケーラブルな協調フィルタリング ソリューションの構築方法については説明しましたが、結果を他のタイプのフィルタリングと組み合わせることで、レコメンデーションを向上させることができます。他の 2 つの主要なフィルタリングのタイプ、コンテンツ ベースとクラスタリングを思い出してください。これらの方法と組み合わせることで、ユーザーにとってより良いレコメンデーションを生成することができます。

フィルタリングの種類

コンテンツ ベースのフィルタリング

コンテンツ ベースのフィルタリングは、アイテムの属性と直接連携し、その類似度を理解することで、属性はあるがユーザーの評価はほとんどないアイテムのレコメンデーションの作成を容易にします。ユーザーベースが成長して、ユーザーの数が増えても、このタイプのフィルタリングは管理しやすいままです。

コンテンツ ベースのフィルタリングを追加するには、カタログ内のアイテムに対する他のユーザーの以前の評価を使用することができます。これらの評価に基づいて、現在の商品に最も類似した商品を見つけることができます。

2 つの商品間の類似度を計算するための一般的な方法の 1 つが、コサイン類似度を使用して、最も近い仲間を見つけることです。

コサイン類似度の式

コサイン類似度の図

類似度の結果は、0~1 の間になります。1 に近いほど、商品の類似度は高くなります。

コサイン類似度のスペクトル

次のマトリックスを考えてみましょう。

コサイン類似度の式

このマトリックスでは、P1 と P2 の類似度は次のように計算できます。

コサイン類似度の式と結果

コンテンツ ベースのフィルタリングは、さまざまなツールを介して利用できます。詳しくは以下をご覧ください。

  • すべてのペアの類似度に関する Twitter。MLlib に追加された CosineSimilarities 関数は、Spark 環境で実行できます。

  • Mahout。いくつかの MLlib アルゴリズムを補完または置き換えるため、より多くのライブラリにアクセスする場合は、インスタンスに接続するための ssh を使用するか、初期化アクションを使用して、Dataproc コントローラ(マスター)ノードに Mahout をインストールします。

    sudo apt-get update
    sudo apt-get install mahout -y
    

クラスタリング

ブラウジング コンテキストとユーザーが現在見ているものを理解することも重要です。同じ人が別の時間に何度が閲覧している場合は、まったく異なる 2 つの商品に興味を持っているか、他の誰かのためのギフトを購入する可能性があります。現在表示されているものと類似しているアイテムを理解できることが非常に重要です。K 平均クラスタリングを使用すると、システムがアイテムのコア属性に基づいて、同様のアイテムをバケットに入れられるようになります。

このソリューションでは、たとえば、東京で賃貸住宅を探している人は、今のところ大阪の賃貸住宅にはおそらく興味はないでしょう。そのため、システムはレコメンデーションを行う際にこれらの物件を除外する必要があります。

from pyspark.mllib.clustering import KMeans, KMeansModel
clusters = KMeans.train(parsedData, 2,
                        maxIterations=10,
                        runs=10,
                        initializationMode="random")

360 度のビュー

過去の注文、サポート、年齢や場所、性別などの個人属性といった、その他の顧客データを考慮に入れることで、レコメンデーションをさらに向上させることができます。これらの属性は、顧客管理システム(CRM)やエンタープライズ リソース プランニング(ERP)システムですでに利用可能になっていることが多く、選択肢を絞り込むのに役立ちます。

さらによく考えてみます。ユーザーの行動や選択に影響を与えるのは、内部システムデータだけではなく、外部要因も影響を与えます。このソリューションの休暇用の賃貸物件のケースでは、若い家族にとっては、その場所の空気の質がわかることも重要かもしれません。そのため、Google Cloud 上に構築されたレコメンデーション エンジンを、Breezometer などの別の API と統合することで、競争上の優位性が得られる可能性があります。