自分で作成したデータセット、TensorFlow を使用した株価予測チュートリアル

自分で作成したデータセット、TensorFlow を使用した株価予測チュートリアル

[[211061]]

STATWORX チームは最近、Google Finance API から S&P 500 データを選択しました。このデータセットには、S&P 500 の指数と株価情報が含まれています。彼らはこのデータを使って、ディープラーニングモデルとその構成銘柄500銘柄の株価を利用してS&P500指数を予測したいと考えている。 STATWORX チームのデータセットは非常に斬新ですが、予測を行うために 4 つの隠れ層を持つ完全接続ネットワークのみを使用しています。読者はデータをダウンロードして、より高度なリカレント ニューラル ネットワークを試すこともできます。

この記事は、TensorFlow を使用して基本的なニューラル ネットワークを構築する方法を理解する初心者に最適です。TensorFlow モデルの構築に関係する概念とモジュールを詳しく説明しています。この記事で使用されているデータセットは直接ダウンロードできるので、ある程度の基礎知識を持つ読者は、より強力なリカレント ニューラル ネットワークを使用してこのタイプの時系列データを処理することもできます。

データセットのアドレス: http://files.statworx.com/sp500.zip

データのインポートと前処理

STATWORX チームはサーバーから株価データをクロールし、csv ファイルとして保存します。このデータセットには、2017 年 4 月から 8 月までの 500 銘柄の株式と S&P 500 指数を網羅した n=41266 分の記録が含まれており、幅広い銘柄と指数をカバーしています。

  1. # データをインポート
  2.  
  3. データ = pd.read_csv( 'data_stocks.csv' )
  4.  
  5. #データセット次元
  6.  
  7. n = データ形状[0]
  8.  
  9. p = データ.shape[1]

データセットはクリーンアップされ、前処理されています。つまり、欠落している株式と指数は LOCF されます (次の観測値は前の観測値をコピーします)。そのため、データセットには欠落値が含まれません。

pyplot.plot('SP500') ステートメントを使用して、S&P 時系列データをプロットできます。

S&P 500 時系列チャート

  • トレーニングデータとテストデータを準備する

データセットはトレーニング データとテスト データに分割する必要があり、トレーニング データにはデータセット全体のレコードの 80% が含まれます。データセットを乱す必要はなく、順番にスライスするだけで済みます。トレーニングデータは2017年4月から2017年7月末まで、テストデータは2017年8月までの残りのデータから選択できます。

  1. # トレーニングテストデータ
  2.  
  3. トレイン開始 = 0
  4.  
  5. トレーニング終了 = int (np.floor(0.8*n))
  6.  
  7. テスト開始 = トレーニング終了 + 1
  8.  
  9. テスト終了 = n
  10.  
  11. data_train = データ[np.arange(train_start, train_end), :]
  12.  
  13. data_test = data[np.arange(test_start, test_end), :]

時系列クロス検証には、再フィッティングの有無にかかわらずローリング予測を実行したり、時系列ブートストラップ再サンプリングなどのより複雑な戦略を実行したりするなど、さまざまな方法があります。後者は、元の時系列と同じ周期パターンを持つサンプルをシミュレートするために、時系列の周期的分解の繰り返しサンプルを伴いますが、これは単に値をコピーするだけではありません。

  • データの標準化

ほとんどのニューラル ネットワーク アーキテクチャでは、tanh や sigmoid などのほとんどのニューロンの活性化関数が [-1, 1] または [0, 1] の区間で定義されているため、正規化されたデータが必要です。現在、ReLU 活性化関数が最も一般的に使用されていますが、その値の範囲には下限はありますが上限はありません。ただし、いずれにしても入力値とターゲット値を再スケールする必要があり、これは勾配降下アルゴリズムの使用にも役立ちます。スケーリングは、sklearn の MinMaxScaler を使用すると簡単に実現できます。

  1. # スケールデータ
  2.  
  3. sklearn.preprocessingからMinMaxScaler をインポートします
  4.  
  5. スケーラー = MinMaxScaler()
  6.  
  7. スケーラー.fit(data_train)
  8.  
  9. スケーラー.transform(data_train)
  10.  
  11. スケーラー.transform(data_test)
  12.  
  13. # Xyを構築
  14.  
  15. X_train = データ_train[:, 1:]
  16.  
  17. y_train = データ_train[:, 0]
  18.  
  19. X_test = データテスト[:, 1:]
  20.  
  21. y_test = data_test[:, 0]pycharm

データのどの部分をいつスケーリングするかを慎重に決定する必要があることに注意してください。よくある間違いは、データセットをテスト データセットとトレーニング データセットに分割する前に、データセット全体をスケーリングすることです。スケーリングを実行するには、変数の最大値や最小値などの統計を計算する必要があるためです。しかし、現実の世界では将来の観測情報は得られないため、トレーニング データに対して比例的に統計計算を実行し、その統計結果をテスト データに適用する必要があります。それ以外の場合は、将来の時系列予測情報を使用することになり、予測メトリックがプラスの側に偏ることがよくあります。

TensorFlow 入門

TensorFlow は優れたフレームワークであり、現在、ディープラーニングとニューラル ネットワークで最も使用されているフレームワークです。基盤となるバックエンドは C++ ですが、通常は Python 経由で制御されます。 TensorFlow は強力な静的グラフを使用して、設計に必要なアルゴリズムと操作を表します。このアプローチにより、ユーザーはグラフ内のノードとして操作を指定し、テンソルの形式でデータを転送して、効率的なアルゴリズム設計を実現できます。ニューラル ネットワークは実際にはデータと数学演算の計算グラフであるため、TensorFlow はニューラル ネットワークとディープラーニングを適切にサポートできます。

一般的に、TensorFlow は、データフロー グラフを使用した数値計算用のオープン ソース ソフトウェア ライブラリです。 Tensor は渡されるデータがテンソル (多次元配列) であることを意味し、Flow は計算グラフを使用して計算することを意味します。データフロー グラフは、「ノード」と「エッジ」で構成される有向グラフを使用して数学演算を記述します。 「ノード」は一般に数学演算を表すために使用されますが、データ入力の開始点と出力の終了点、または永続変数の読み取り/書き込みの終了点を表すこともできます。エッジはノード間の入力/出力関係を表します。これらのデータ エッジは、動的に調整可能な次元、つまりテンソルを持つ多次元データ配列を送信できます。

加算を実行する単純な計算グラフ

上の図では、2 つの 0 次元テンソル (スカラー) が追加され、2 つの変数 a と b に格納されています。これら 2 つの値はグラフを流れ、四角いノードに到達すると加算され、加算の結果が変数 c に格納されます。実際、a、b、c はプレースホルダーとして見ることができ、a と b に入力された値はすべて c に追加されます。これが TensorFlow の基本原理です。ユーザーはプレースホルダーと変数を通じてモデルの抽象表現を定義し、プレースホルダーに実際のデータを入力して実際の操作を生成できます。次のコードは、上図の単純な計算グラフを実装します。

  1. # TensorFlow をインポートする
  2.  
  3. テンソルフローをtfとしてインポートする
  4.  
  5. # ab をプレースホルダーとして定義する
  6.  
  7. a = tf.placeholder(dtype=tf.int8)
  8.  
  9. b = tf.placeholder(dtype=tf.int8)
  10.  
  11. # 追加を定義する
  12.  
  13. c = tf.add (a, b)
  14.  
  15. # グラフを初期化する
  16.  
  17. グラフ = tf.Session()
  18.  
  19. # グラフを実行する
  20.  
  21. graph.run(c, feed_dict{a: 5, b: 4})

上記のように、TensorFlow ライブラリをインポートした後、tf.placeholder() を使用して、テンソル a と b を事前に保存するための 2 つのプレースホルダーを定義します。操作を定義した後、操作グラフを実行して結果を取得できます。

  • プレースホルダー

前述したように、ニューラル ネットワークは最初はプレースホルダーから派生します。したがって、モデルに適合させるために 2 つのプレースホルダーを定義する必要があります。X にはニューラル ネットワークへの入力 (T=t の時点でのすべての S&P 500 株価) が含まれ、Y にはニューラル ネットワークの出力 (T=t+1 の時点での S&P 500 のインデックス値) が含まれます。

したがって、入力データ プレースホルダーの次元は [None, n_stocks] として定義でき、出力プレースホルダーの次元は [None] となり、それぞれ 2 次元テンソルと 1 次元テンソルを表します。入力テンソルと出力テンソルの次元を理解することは、ニューラル ネットワーク全体を構築する上で非常に重要です。

  1. # プレースホルダー
  2.  
  3. X = tf.placeholder(dtype=tf.float32, shape=[なし, n_stocks])
  4.  
  5. Y = tf.placeholder(dtype=tf.float32, shape=[なし])

上記のコードの None は、ニューラル ネットワークに渡される各バッチの数はまだわからないことを意味します。そのため、None を使用すると柔軟性を維持できます。各トレーニングで使用されるバッチ サイズを制御するために、後で batch_size を定義します。

  • 変数

プレースホルダーに加えて、変数はデータと操作を表す TensorFlow のもう 1 つの重要な要素です。プレースホルダーは、入力データと出力データを格納するために計算グラフ内でよく使用されますが、変数は計算グラフ内の非常に柔軟なコンテナーであり、実行中に変更したり渡したりすることができます。ニューラル ネットワークの重みとバイアスは、トレーニング中に簡単に調整できるように、通常、変数を使用して定義されます。変数は初期化する必要があり、これについては後で詳しく説明します。

このモデルは 4 つの隠し層で構成され、最初の層には 1024 個のニューロンが含まれ、次の 3 つの層は 2 の倍数で減少し、それぞれ 512、256、128 個のニューロンになります。後続の層のニューロンの数は順次削減され、前の層で抽出された特徴が圧縮されます。もちろん、他のニューラル ネットワーク アーキテクチャやニューロン構成を使用して、データをより適切に処理することもできます。たとえば、畳み込みニューラル ネットワーク アーキテクチャは画像データの処理に適しており、再帰型ニューラル ネットワークは時系列データの処理に適しています。ただし、この記事は、完全接続ネットワークを使用して時系列データを処理する方法について初心者向けに簡単に紹介しただけなので、この記事ではそれらの複雑なアーキテクチャについては説明しません。

  1. # モデルアーキテクチャパラメータ
  2.  
  3. 在庫数 = 500
  4.  
  5. ニューロン数1 = 1024
  6.  
  7. ニューロン数2 = 512
  8.  
  9. ニューロン数3 = 256
  10.  
  11. ニューロン数4 = 128
  12.  
  13. n_ターゲット = 1
  14.  
  15. # レイヤー 1:隠れた重みバイアス変数
  16.  
  17. W_hidden_​​1 = tf.Variable(weight_initializer([n_stocks, n_neurons_1]))
  18.  
  19. バイアス_hidden_​​1 = tf.Variable(バイアス_initializer([n_neurons_1]))
  20.  
  21. # レイヤー2:隠れた重みバイアス変数
  22.  
  23. W_hidden_​​2 = tf.Variable(weight_initializer([n_neurons_1, n_neurons_2]))
  24.  
  25. バイアス_hidden_​​2 = tf.Variable(バイアス_initializer([n_neurons_2]))
  26.  
  27. # レイヤー3:隠れた重みバイアス変数
  28.  
  29. W_hidden_​​3 = tf.Variable(weight_initializer([n_neurons_2, n_neurons_3]))
  30.  
  31. バイアス_hidden_​​3 = tf.Variable(バイアス_initializer([n_neurons_3]))
  32.  
  33. # レイヤー4:隠れた重みバイアス変数
  34.  
  35. W_hidden_​​4 = tf.Variable(weight_initializer([n_neurons_3, n_neurons_4]))
  36.  
  37. バイアス_hidden_​​4 = tf.Variable(バイアス_initializer([n_neurons_4]))
  38. #出力層:変数 出力の重みバイアス
  39.  
  40. W_out = tf.Variable(weight_initializer([n_neurons_4, n_target]))
  41.  
  42. バイアス出力 = tf.Variable(バイアス初期化子([n_target]))

入力層、隠れ層、出力層間の変数の次元変換を理解することは、ネットワーク全体を理解する上で非常に重要です。多層パーセプトロンの経験則として、後の層の最初の次元は、前の層の重み変数の 2 番目の次元に対応します。複雑に聞こえるかもしれませんが、実際には各レイヤーの出力を次のレイヤーへの入力として渡すだけです。バイアス項の次元は、現在の層の重みの 2 番目の次元に等しく、その層内のニューロンの数にも等しくなります。

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

ニューラル ネットワークに必要な重み行列とバイアス ベクトルを定義した後、ニューラル ネットワークのトポロジまたはネットワーク アーキテクチャを指定する必要があります。したがって、プレースホルダー (データ) と変数 (重みとバイアス) を、連続した行列乗算システムに組み合わせる必要があります。

さらに、ネットワークの隠れ層の各ニューロンにも、非線形変換のための活性化関数が必要です。活性化関数は、システムに非線形性を導入するため、ネットワーク アーキテクチャの非常に重要なコンポーネントです。現在、多くの活性化関数が存在しますが、最も一般的なのは、このモデルでも使用される、正規化線形ユニット (ReLU) 活性化関数です。

  1. # 隠しレイヤー
  2.  
  3. hidden_​​1 = tf.nn.relu( tf.add (tf.matmul(X, W_hidden_​​1),bias_hidden_​​1))
  4.  
  5. hidden_​​2 = tf.nn.relu( tf.add (tf.matmul(hidden_​​1, W_hidden_​​2),bias_hidden_​​2))
  6.  
  7. hidden_​​3 = tf.nn.relu( tf.add (tf.matmul(hidden_​​2, W_hidden_​​3),bias_hidden_​​3))
  8.  
  9. hidden_​​4 = tf.nn.relu( tf.add (tf.matmul(hidden_​​3, W_hidden_​​4),bias_hidden_​​4))
  10.  
  11. #出力層(転置する必要があります)
  12.  
  13. 出力= tf.transpose(tf.add ( tf.matmul(hidden_​​4, W_out),bias_out))

次の図は、この記事で構築したニューラル ネットワーク アーキテクチャを示しています。このモデルは、主に入力層、隠し層、出力層の 3 つの構成要素で構成されています。このアーキテクチャは、フィードフォワード ネットワークまたは完全接続ネットワークと呼ばれます。フィードフォワードとは、入力バッチ データが左から右にのみ流れることを意味します。リカレント ニューラル ネットワークなどの他のアーキテクチャでは、データが逆方向に流れることもできます。

フィードフォワードネットワークのコアアーキテクチャ

  • 損失関数

このネットワークの損失関数は主に、ネットワーク予測と実際の観測されたトレーニング ターゲット間の偏差値を生成するために使用されます。回帰問題では、平均二乗誤差 (MSE) 関数が最も一般的に使用されます。 MSE は予測値と目標値の間の平均二乗誤差を計算します。

  1. # コスト関数 
  2.  
  3. mse = tf.reduce_mean(tf.squared_difference(出力、Y))

しかし、MSE の特性は一般的な最適化問題では非常に有利です。

  • オプティマイザ

オプティマイザーは、トレーニング中にネットワークの重みとバイアス変数を適応させるために必要な計算を処理します。これらの計算には勾配計算が必要であり、ネットワークのコスト関数を最小限に抑えるためにトレーニング中に重みとバイアスをどの方向に変更する必要があるかを示します。安定した高速なオプティマイザーの開発は、ニューラル ネットワークとディープラーニングの分野において常に重要な研究となっています。

  1. # オプティマイザー
  2.  
  3. opt = tf.train.AdamOptimizer().minimize(mse)

上記では、ディープラーニングのデフォルトのオプティマイザーである Adam オプティマイザーを使用しています。 Adam は Adaptive Moment Estimation の略で、2 つの最適化ツール AdaGrad と RMSProp の組み合わせと考えることができます。

  • 初期化子

初期化子は、トレーニング前にネットワークの変数を初期化するために使用されます。ニューラル ネットワークは数値最適化手法を使用してトレーニングされるため、最適化問題の開始点は適切なソリューションを見つけることに焦点が当てられます。 TensorFlow にはさまざまな初期化子があり、それぞれ初期化方法が異なります。この投稿では、デフォルトの初期化戦略である tf.variance_scaling_initializer() を使用します。

  1. # 初期化子
  2.  
  3. シグマ =
  4.  
  5. weight_initializer = tf.variance_scaling_initializer(モード = "fan_avg" 、分布 = "uniform" 、スケール = sigma)
  6.  
  7. バイアス初期化子 = tf.zeros_initializer()

TensorFlow 計算グラフでは、異なる変数に対して複数の初期化関数を定義できることに注意してください。ただし、ほとんどの場合、単一の統合された初期化関数で十分です。

ニューラルネットワークのフィッティング

ネットワークのプレースホルダー、変数、初期化子、コスト関数、およびオプティマイザーを定義したら、通常はミニバッチ トレーニングを使用してモデルのトレーニングを開始できます。ミニバッチトレーニング中、n = batch_size のデータサンプルがトレーニングデータからランダムに抽出され、ネットワークに送られます。トレーニング データセットは n/batch_size のバッチに分割され、順番にネットワークに供給されます。この時点で、プレースホルダー X と Y が機能します。これらは入力データとターゲット データを格納し、ネットワーク内でそれぞれ入力とターゲットとして表されます。

X からのデータのバッチは、出力層に到達するまでネットワークを介して前方に流れます。出力層では、TensorFlow は現在のバッチのモデル予測と実際の観測ターゲット Y を比較します。次に、TensorFlow は最適化を実行し、選択された学習スキームを使用してネットワークのパラメータを更新します。重みとバイアスを更新した後、次のバッチがサンプリングされ、プロセスが繰り返されます。このプロセスは、すべてのバッチがネットワークに投入され、1 つのエポックが完了するまで継続されます。

トレーニングがエポックの最大値またはその他のユーザー定義の停止基準に達すると、ネットワークのトレーニングは停止します。

  1. # 初期化子を実行する
  2.  
  3. net.run(tf.global_variables_initializer())
  4.  
  5.  
  6.  
  7. # インタラクティブプロットを設定する
  8.  
  9. plt.ion()
  10.  
  11. 図 = plt.figure()
  12.  
  13. ax1 = 図.add_subplot(111)
  14.  
  15. ライン1、= ax1.plot(y_test)
  16.  
  17. 行2、= ax1.plot(y_test*0.5)
  18.  
  19. plt.show()
  20.  
  21.  
  22.  
  23. #エポックバッチサイズ 
  24.  
  25. エポック = 10
  26.  
  27. batch_size = 256、e範囲(エポック):
  28.  
  29.  
  30.  
  31. # トレーニングデータをシャッフルする
  32.  
  33. shuffle_indices = np.random.permutation(np.arange(len(y_train)))
  34.  
  35. X_train = X_train[シャッフルインデックス]
  36.  
  37. y_train = y_train[シャッフルインデックス]
  38.  
  39.  
  40.  
  41. # ミニバッチトレーニング
  42.  
  43. irange(0, len(y_train) // batch_size)場合:
  44.  
  45. 開始 = i * バッチサイズ
  46.  
  47. batch_x = X_train[開始:開始 + batch_size]
  48.  
  49. batch_y = y_train[開始:開始 + batch_size]
  50.  
  51. #バッチオプティマイザを実行する
  52.  
  53. net.run(opt, feed_dict={X: batch_x, Y: batch_y})
  54.  
  55.  
  56.  
  57. # 進捗状況を表示
  58.  
  59. np.mod(i, 5) == 0の場合:
  60.  
  61. # 予測
  62.  
  63. pred = net.run(出力、 feed_dict={X: X_test})
  64.  
  65. 2行目.set_ydata(予測)
  66.  
  67. plt.title( 'エポック ' + str(e) + ', バッチ ' + str(i))
  68.  
  69. ファイル名 = 'img/epoch_' + str(e) + '_batch_' + str(i) + '.jpg'  
  70.  
  71. plt.savefig(ファイル名)
  72.  
  73. plt.一時停止(0.01)

トレーニングプロセス中、5 回のトレーニングごとに 1 回、テスト セット (ネットワークによって学習されていないデータ) に対するネットワークの予測能力を評価し、結果を表示しました。さらに、これらの画像はディスクにエクスポートされ、トレーニング プロセスのビデオ アニメーションに結合されます。モデルはテスト データ内の時系列の位置と形状をすばやく学習し、数回のトレーニングの後に正確な予測を生成します。素晴らしい!

ご覧のとおり、ネットワークは時系列の基本的な形状に素早く適応し、データ内のより細かいパターンを学習し続けます。これは、最小値を見逃さないようにモデルトレーニング中に学習率を下げる Adam 学習スキームによるものです。 10 エポック後、テスト データが完全に適合しました。最良のテスト MSE は 0.00078 に等しく、ターゲットがスケーリングされているため非常に低い値です。テスト セットの予測の平均パーセンテージ誤差は 5.31% であり、これは非常に良好な結果です。

S&P の予測価格と実際の価格の散布図 (拡大)

この結果をさらに最適化する方法は多数あることに注意してください。レイヤーとニューロンの設計、さまざまな初期化およびアクティベーション スキームの選択、ニューロンのドロップアウト レイヤーの導入、早期停止の適用などです。さらに、リカレントニューラルネットワークなどの他の異なるタイプのディープラーニングモデルでは、このタスクでより良い結果を達成できる可能性があります。しかし、これは私たちの議論の範囲を超えています。

結論と展望

TensorFlow のリリースは、ディープラーニング研究における画期的な出来事です。その高い柔軟性と強力なパフォーマンスにより、研究者はあらゆる種類の複雑なニューラル ネットワーク アーキテクチャやその他の機械学習アルゴリズムを開発できます。ただし、柔軟性の代償として、Keras や MxNet などの高レベル API を使用する場合と比べてモデリング時間が長くなります。それにもかかわらず、TensorFlow は進化を続け、ニューラル ネットワークとディープラーニング開発の研究と実用化の両方において事実上の標準になると信じています。弊社のお客様の多くはすでに TensorFlow を使用しているか、TensorFlow モデルを適用するプロジェクトを開発しています。 STATWORX (https://www.statworx.com/de/data-science/) のデータ サイエンス コンサルタントは、基本的に TensorFlow 研究コースを使用してディープラーニングとニューラル ネットワークを開発しています。

Google の TensorFlow に関する今後の計画は何ですか?少なくとも私の意見では、TensorFlow には、TensorFlow バックエンドでニューラル ネットワーク アーキテクチャを設計および開発するための優れたグラフィカル ユーザー インターフェイスが欠けています。おそらくこれが Google の将来の目標なのでしょう。

<<:  ブラックボックス問題が依然としてディープラーニングの普及を妨げている

>>:  李開復:今後数年間、中国で最も収益性の高い仕事は何でしょうか?

ブログ    
ブログ    
ブログ    
ブログ    

推薦する

マスク氏も騙された。AIの虚偽の内容が「リアル」すぎる

イスラエルとパレスチナの紛争が深刻化するにつれ、ソーシャルメディアのプラットフォーム上には現地の情景...

コンピュータービジョンにおける AI の役割は何ですか?

コンピュータービジョン技術を使用することで、コンピューターは視覚的に物を識別したり確認したりすること...

中国科学院のチームは、最初のLLMモデル圧縮レビューを発表しました。剪定、知識蒸留、量子化技術の詳細な議論です。

最近、大規模言語モデル (LLM) はさまざまなタスクで優れたパフォーマンスを示しています。しかし、...

ディープラーニングの一般化能力についての簡単な議論

1. DNNの一般化能力に関する問題この論文では主に、過剰パラメータ化されたニューラル ネットワー...

中国科学院とディープマインドが協力し、ディープラーニングを使って脳が顔を認識する仕組みを解明

この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI)より許可を得て転載...

...

顔認証ロック解除を使用するとき、携帯電話はどのようにしてあなたを「認識」するのでしょうか?顔認識について詳しく知る

2020年10月1日、私たちの祖国は71歳の誕生日を迎えました!我が国は、最初の人工衛星の打ち上げか...

百度は「ニューラル条件付きランダムフィールド」病理スライス分析アルゴリズムをオープンソース化、専門病理学者よりも高い精度を実現

最近、百度研究所は論文で「ニューラル条件付きランダムフィールド」病理スライス分析アルゴリズムを提案し...

シティグループは5年以内に1万人の雇用を人工知能で置き換える計画

[[233047]]フィナンシャル・タイムズによると、シティグループは5年以内に投資銀行部門の技術・...

大規模言語モデルに基づくインテリジェントエージェントのモデリングとシミュレーション:レビューと展望

この記事は、Heart of Autonomous Driving の公開アカウントから許可を得て転...

...

...

IDCは、年平均成長率31.4%で、世界のAIソフトウェアの収益は2027年に2,790億ドルに達すると予測している。

11月2日、市場調査会社IDCが発表した最新の予測レポートによると、世界のAIソフトウェア市場規模...