機械学習: Python でベイズ分類器をゼロから実装する

機械学習: Python でベイズ分類器をゼロから実装する

ナイーブ ベイズ アルゴリズムはシンプルで効率的であり、分類問題を扱う際に最初に検討すべき方法の 1 つです。

このチュートリアルでは、Naive Bayes アルゴリズムの原理と、Python バージョンの段階的な実装を学習します。

更新: ナイーブベイズを使用するためのヒントに関する私のフォローアップ記事をご覧ください: より優れたナイーブベイズ: ナイーブベイズアルゴリズムを最大限に活用するための 12 のヒント

[[194741]]

ナイーブベイズ分類器、一部の著作権は Matt Buck が保有しています

ナイーブベイズについて

ナイーブ ベイズ アルゴリズムは、各属性が特定のクラスに属する確率を使用して予測を行う直感的な方法です。この教師あり学習アプローチを使用して、予測モデリングの問題に対して確率モデリングを実行できます。

クラスが与えられた場合、ナイーブ ベイズでは、各属性がこのクラスに属する確率は他のすべての属性から独立していると想定され、確率の計算が簡素化されます。この強力な仮定により、高速かつ効率的な方法が実現します。

属性値が与えられた場合に、それが特定のクラスに属する確率を条件付き確率と呼びます。特定のクラス値に対して、各属性の条件付き確率を掛け合わせると、データ サンプルが特定のクラスに属する確率が得られます。

これは、サンプルが各クラスに属する確率を計算し、最も高い確率を持つクラスを選択することで実行できます。

通常、比率を使用して説明および計算するのが簡単なため、Naive Bayes を説明するためにカテゴリ データを使用します。私たちの目的にとって有用なアルゴリズムは、数値属性をサポートし、各数値属性が正規分布(ベル曲線上に分布)に従うと仮定する必要があります。これはもう 1 つの強力な仮定ですが、それでも堅牢な結果が得られます。

糖尿病の発症を予測する

この記事で使用されているテスト問題は、「ピマ・インディアン糖尿病問題」です。

この号には、ピマ・インディアンの患者768人に対する医学的観察の詳細が掲載されており、患者の年齢、妊娠、血液検査の回数など、行われた瞬間的な測定値を記した記録がある。すべての患者は 21 歳以上の女性であり、すべての属性は数値であり、属性の単位は異なります。

各レコードは、測定時点から 5 年以内に患者が糖尿病と診断されたかどうかを示すクラスに属します。はいの場合は 1、そうでない場合は 0 です。

この標準データセットは機械学習の文献で何度も研究されており、予測精度は 70% ~ 76% と良好です。

以下は、使用するデータの概要を示す pima-indians.data.csv ファイルのサンプルです。

注意: ファイルをダウンロードし、.csv 拡張子で保存します (例: pima-indians-diabetes.data.csv)。ファイル内のすべてのプロパティの説明を表示します。

  1. 6,148,72,35,0,33.6,0.627,50,1
  2.  
  3. 1,85,66,29,0,26.6,0.351,31,0
  4.  
  5. 8,183,64,0,0,23.3,0.672,32,1
  6.  
  7. 1,89,66,23,94,28.1,0.167,21,0
  8.  
  9. 0,137,40,35,168,43.1,2.288,33,1

ナイーブベイズアルゴリズムのチュートリアル

チュートリアルは次の手順に分かれています。

1. データを処理する: CSV ファイルからデータを読み込み、トレーニング セットとテスト セットに分割します。

2. データの特徴を抽出する: 確率を計算して予測を行えるように、トレーニング データ セットの属性の特徴を抽出します。

3. 単一の予測: データセットの特徴を使用して単一の予測を生成します。

4. 複数の予測: 指定されたテスト データセットと、抽出された特徴を含むトレーニング データセットに基づいて予測を生成します。

5. 評価精度: テストデータセットの予測精度を予測の正確さとして評価します。

6. 結合されたコード: すべてのコードを使用して、Naive Bayes アルゴリズムの完全なスタンドアロン実装を提示します。

1. データ処理

まずデータファイルをロードします。 CSV 形式のデータにはヘッダー行と引用符がありません。 csv モジュールの open 関数を使用してファイルを開き、 reader 関数を使用して行データを読み取ることができます。

また、文字列として読み込まれた属性を、使用できる数値に変換する必要があります。以下は、ピマ族のデータセットをロードするために使用される loadCsv() 関数です。

  1. csvをインポート
  2.  
  3. def loadCsv(ファイル名):
  4.  
  5. 行 = csv.reader( open (ファイル名、 "rb" ))
  6.  
  7. データセット = リスト(行)
  8.  
  9. i が範囲(len(データセット))内にある場合:
  10.  
  11. データセット[i] = [ float (x)データセット[i]内のx ]
  12.  
  13. データセットを返す

この関数をテストするには、Pima Indians データセットをロードし、データ サンプルの数を出力します。

  1. ファイル名 = 'pima-indians-diabetes.data.csv'  
  2.  
  3. データセット = loadCsv(ファイル名)
  4.  
  5. print( '{1} 行のデータ ファイル {0} がロードされました' ).format(filename, len(dataset))

テストを実行すると、次の結果が表示されます。

  1. 150データファイルiris.data.csvをロードしました 

次に、データを Naive Bayes 予測用のトレーニング データセットとモデルの精度を評価するためのテスト データセットに分割します。データセットを、67% を含むトレーニング セットと 33% を含むテスト セットにランダムに分割する必要があります (これは、このデータセットでアルゴリズムをテストする場合の通常の比率です)。

以下は、指定された分割比率でデータセットを 2 つの部分に分割する splitDataset() 関数です。

  1. ランダムにインポート
  2.  
  3. def splitDataset(データセット、分割比率):
  4.  
  5. トレーニングサイズ = int (len(データセット) * 分割比率)
  6.  
  7. トレーニングセット = []
  8.  
  9. コピー = リスト(データセット)
  10.  
  11. len(trainSet) < trainSize の場合:
  12.  
  13. インデックス= random.randrange(len(コピー))
  14.  
  15. trainSet.append(コピー.pop(インデックス))
  16.  
  17. [trainSet, コピー]を返す

テスト用に 5 つのサンプルを含むデータ セットを定義できます。まず、トレーニング データ セットとテスト データ セットに分割し、各データ サンプルが最終的にどのデータ セットに分類されるかを確認するために印刷します。

  1. データセット = [[1], [2], [3], [4], [5]]
  2.  
  3. 分割比率 = 0.67
  4.  
  5. トレーニング、テスト = splitDataset(データセット、splitRatio)
  6.  
  7. print( '{0} 行を {1} でトレーニングし、{2} でテストする' ).format(len(dataset), train, test)

テストを実行すると、次の結果が表示されます。

  1. 5に分割  [[4]、[3]、[5]]訓練 [[1]、[2]]テストする

データ特徴の抽出

Naive Bayes モデルは、トレーニング データセット内のデータの機能を組み込み、このデータ機能を使用して予測を行います。

収集されたトレーニング データの特徴には、各クラスに関する各属性の平均と標準偏差が含まれます。たとえば、クラスが 2 つあり、数値属性が 7 つある場合、属性 (7) とクラス (2) の各組み合わせの平均と標準偏差、つまり 14 個の属性機能が必要になります。

これらの特徴は、特定の属性が各クラスに属する確率を計算および予測するときに使用されます。

データ特徴の取得を次のサブタスクに分割します。

  1. データをカテゴリ別に分ける
  2. 平均を計算する
  3. 標準偏差を計算する
  4. データセットから特徴を抽出する
  5. カテゴリ別に属性特徴を抽出する

データをカテゴリ別に分ける

まず、トレーニング データ セット内のサンプルがカテゴリに分割され、各クラスの統計が計算されます。カテゴリからそのカテゴリに属する​​例のリストへのマッピングを作成し、データセット全体の例を対応するリストに分類できます。

次の SeparateByClass() 関数はこのタスクを実行できます。

  1. def SeparateByClass(データセット):
  2.  
  3. 区切る = {}
  4.  
  5. i が範囲(len(データセット))内にある場合:
  6.  
  7. ベクトル = データセット[i]
  8.  
  9. if (vector[-1]ではない 分離し):
  10.  
  11. 分離された[ベクトル[-1]] = []
  12.  
  13. 分離された[ベクトル[-1]].append(ベクトル)
  14.  
  15. 分離して返す

この関数は、サンプル内の最後の属性 (-1) がカテゴリ値であると想定し、カテゴリ値からデータ サンプルのリストへのマッピングを返すことがわかります。

次のようにサンプルデータを使用してこれをテストできます。

  1. データセット = [[1,20,1], [2,21,0], [3,22,1]]
  2.  
  3. 分離された = クラスごとに分離(データセット)
  4.  
  5. print( '分離されたインスタンス: {0}' ).format(separated)

テストを実行すると、次の結果が表示されます。

  1. 分離されたインスタンス: {0: [[2, 21, 0]], 1: [[1, 20, 1], [3, 22, 1]]}

平均を計算する

各クラスの各属性の平均を計算する必要があります。平均はデータの中間点または中心傾向であり、確率を計算するときにガウス分布の中央値として使用されます。

また、各クラス内の各属性の標準偏差を計算する必要があります。標準偏差は、データ分布の偏差を表します。確率を計算するときは、ガウス分布の各属性の予想される分散を特徴付けるために使用します。

標準偏差は分散の平方根です。分散は、各属性値の平均からの偏差の二乗の平均です。 N-1 メソッドを使用することに注意してください。つまり、分散を計算するときに、属性値の数が 1 減少します。

  1. インポート数学
  2.  
  3. def 平均(数値):
  4.  
  5. 戻る 合計(数値) /浮動小数点数 (長さ (数値))
  6.  
  7.   
  8.  
  9. def stdev(数値):
  10.  
  11. 平均= 平均(数値)
  12.  
  13. 分散 = sum ([pow(x- avg ,2) for x in numbers])/ float (len(numbers)-1)
  14.  
  15. math.sqrt(variance)を返す

1 から 5 までの 5 つの数字の平均を計算して関数をテストします。

  1. 数字 = [1,2,3,4,5]
  2.  
  3. print( '{0} の概要: 平均={1}、標準偏差={2}' ).format(numbers, 平均(numbers), 標準偏差(numbers))

テストを実行すると、次の結果が表示されます。

  1. [1, 2, 3, 4, 5]要約: 平均=3.0、標準偏差=1.58113883008

データセットから特徴を抽出する

これでデータセットから特徴を抽出できるようになりました。特定のクラスに対応するサンプルのリストに対して、各属性の平均と標準偏差を計算できます。

zip 関数は、データ サンプルを属性に応じてリストにグループ化し、各属性の平均と標準偏差を計算します。

  1. def 要約(データセット):
  2.  
  3. 要約 = [(平均(属性)、標準偏差(属性)) zip(*データセット)]内の属性
  4.  
  5. 要約[-1]
  6.  
  7. 返品概要

最初のデータ属性と 2 番目のデータ属性の平均と標準偏差に大きな差があることを示すテスト データを使用して、この summary() 関数をテストできます。

  1. データセット = [[1,20,0], [2,21,1], [3,22,0]]
  2.  
  3. summary = 要約(データセット)
  4.  
  5. print( '属性サマリー: {0}' ).format(summary)

テストを実行すると、次の結果が表示されます。

  1. 属性の概要: [(2.0, 1.0), (21.0, 1.0)]

カテゴリ別に属性特徴を抽出する

コードをマージします。まずトレーニング データセットをカテゴリに分割し、次に各属性の概要を計算します。

  1. def summaryByClass(データセット):
  2.  
  3. 分離された = クラスごとに分離(データセット)
  4.  
  5. 要約 = {}
  6.  
  7. classValueの場合 separated.iteritems()内のインスタンス:
  8.  
  9. summaries[classValue] = 要約(インスタンス)
  10.  
  11. 返品概要

小さなテスト データセットを使用して、summaryByClass() 関数をテストします。

  1. データセット = [[1,20,1], [2,21,0], [3,22,1], [4,22,0]]
  2.  
  3. summary = summaryByClass(データセット)
  4.  
  5. print( 'クラス値による要約: {0}' ).format(summary)

テストを実行すると、次の結果が表示されます。

  1. クラス値による概要:
  2.  
  3. {0: [(3.0, 1.4142135623730951), (21.5, 0.7071067811865476)],
  4.  
  5. 1: [(2.0, 1.4142135623730951), (21.0, 1.4142135623730951)]}

予測する

これで、トレーニング データから取得した要約を使用して予測を行うことができます。予測を行うには、特定のデータ サンプルについて、各クラスに属する確率を計算し、最も高い確率を持つクラスを予測結果として選択する必要があります。

この部分は次のタスクに分けることができます。

  1. ガウス確率密度関数を計算する
  2. 対応するクラスの確率を計算する
  3. 単一の予測
  4. 評価精度

ガウス確率密度関数を計算する

トレーニング データからの属性の既知の平均と標準偏差が与えられた場合、ガウス関数を使用して特定の属性値の確率を推定できます。

各属性の属性特性とクラス値が与えられると、特定のクラス値の条件下で特定の属性値の条件付き確率を取得できます。

ガウス確率密度関数については参考文献を参照してください。要約すると、既知の詳細 (属性値、平均、標準偏差) をガウス関数に組み込み、属性値が特定のクラスに属する尤度 (翻訳者注: 可能性) を取得する必要があります。

calculateProbability() 関数では、まず指数部分を計算し、次に方程式の幹を計算します。これにより、2 行にきれいに整理されます。

  1. インポート数学
  2.  
  3. def calculateProbability(x, 平均, 標準偏差):
  4.  
  5. 指数= math.exp (-(math.pow(x-mean,2)/(2*math.pow(stdev,2))))
  6.  
  7. (1 / (math.sqrt(2*math.pi) * stdev)) * 指数を返す

次のように簡単なデータを使用してテストします。

  1. x = 71.5
  2.  
  3. 平均 = 73
  4.  
  5. 標準偏差 = 6.2
  6.  
  7. 確率 = calculateProbability(x, 平均, 標準偏差)
  8.  
  9. print( 'このクラスに属する確率: {0}' ).format(確率)

テストを実行すると、次の結果が表示されます。

  1. このクラス属する確率: 0.0624896575937

クラスに属する確率を計算する

属性が特定のクラスに属する確率を計算できるため、データ サンプル内のすべての属性の確率を組み合わせて、最終的にデータ サンプル全体が特定のクラスに属する確率を取得できます。

確率を組み合わせるには乗算を使用します。以下の calculClassProbilities() 関数では、データ サンプルが与えられると、その属性の確率を乗算することで、それが属する各カテゴリの確率を取得できます。結果は、クラス値から確率へのマッピングです。

  1. def calculateClassProbabilities(要約、入力ベクトル):
  2.  
  3. 確率 = {}
  4.  
  5. summaries.iteritems()内のclassValue、classSummariesの場合:
  6.  
  7. 確率[クラス値] = 1
  8.  
  9. i が範囲(len(classSummaries))内である場合:
  10.  
  11. 平均、標準偏差 = classSummaries[i]
  12.  
  13. x = 入力ベクトル[i]
  14.  
  15. 確率[classValue] *= calculateProbability(x, 平均, 標準偏差)
  16.  
  17. リターン確率

calculateClassProbabilities() 関数をテストします。

  1. 要約 = {0:[(1, 0.5)], 1:[(20, 5.0)]}
  2.  
  3. 入力ベクトル = [1.1, '?' ]
  4.  
  5. 確率 = calculateClassProbabilities(要約、入力ベクトル)
  6.  
  7. print( '各クラスの確率: {0}' ).format(確率)

テストを実行すると、次の結果が表示されます。

  1. 各クラス確率: {0: 0.7820853879509118, 1: 6.298736258150442e-05}

単一の予測

データ サンプルが各クラスに属する確率を計算できるようになったので、最大確率値を見つけて、関連するクラスを返すことができます。

次の predict() 関数は上記のタスクを実行できます。

  1. def predict(要約、入力ベクトル):
  2.  
  3. 確率 = calculateClassProbabilities(要約、入力ベクトル)
  4.  
  5. bestLabel、bestProb = なし、-1
  6.  
  7. classValueの場合 probabilities.iteritems()確率:
  8.  
  9. bestLabelNoneまたは確率> bestProbの場合:
  10.  
  11. bestProb = 確率
  12.  
  13. ベストラベル = クラス値
  14.  
  15. ベストラベルを返す

次のようにpredict()関数をテストします。

  1. 要約 = { 'A' :[(1, 0.5)], 'B' :[(20, 5.0)]}
  2.  
  3. 入力ベクトル = [1.1, '?' ]
  4.  
  5. 結果 = 予測(要約、入力ベクトル)
  6.  
  7. print( '予測: {0}' ).format(result)

テストを実行すると、次の結果が得られます。

  1. 予測: A

複数の予測

最後に、テスト データセット内の各データ サンプルに対して予測を行うことで、モデルの精度を評価できます。 getPredictions() 関数はこれを実行し、各テスト サンプルの予測のリストを返します。

  1. def getPredictions(サマリー、テストセット):
  2.  
  3. 予測 = []
  4.  
  5. i が範囲(len(testSet))内にある場合:
  6.  
  7. 結果 = 予測(要約、テストセット[i])
  8.  
  9. 予測.append(結果)
  10.  
  11. リターン予測

getPredictions() 関数を次のようにテストします。

  1. 要約 = { 'A' :[(1, 0.5)], 'B' :[(20, 5.0)]}
  2.  
  3. テストセット = [[1.1, '?' ], [19.1, '?' ]]
  4.  
  5. 予測 = getPredictions(要約、テストセット)
  6.  
  7. print( '予測: {0}' ).format(予測)

テストを実行すると、次の結果が表示されます。

  1. 予測: [ 'A' 'B' ]

計算精度

予測値をテストデータセット内のカテゴリ値と比較することで、分類精度として 0% ~ 100% の精度を計算できます。 getAccuracy() 関数はこの精度を計算できます。

  1. def getAccuracy(テストセット、予測):
  2.  
  3. 正解 = 0
  4.  
  5. xが範囲(len(testSet))の場合:
  6.  
  7. testSet[x][-1] == predictions[x]の場合:
  8.  
  9. 正解 += 1
  10.  
  11. 戻り値(正しい / float (len(testSet))) * 100.0

次の簡単なコードを使用して、getAccuracy() 関数をテストできます。

  1. テストセット = [[1,1,1, 'a' ], [2,2,2, 'a' ], [3,3,3, 'b' ]]
  2.  
  3. 予測 = [ 'a' , 'a' , 'a' ]
  4.  
  5. 精度 = getAccuracy(テストセット、予測)
  6.  
  7. print( '精度: {0}' ).format(精度)

テストを実行すると、次の結果が得られます。

  1. 精度: 66.6666666667

コードをマージする

最後に、コードを連結する必要があります。

以下は、Python で Naive Bayes を段階的に実装するための完全なコードです。

例を実行すると、次の出力が得られます。

  1. 768を分割 トレーニング=514行、テスト=254 
  2.  
  3. 精度: 76.3779527559%

拡張機能の実装

このセクションでは、実装した Python コードを使用して応用研究を実施するためのチュートリアルの一部として使用できる拡張のアイデアを提供します。

この時点で、Python を使用して、Naive Bayes のガウス バージョンを段階的に完了しました。

アルゴリズムの実装をさらに拡張することができます。

クラスに属する確率を計算します。データ サンプルが各クラスに属する確率を比率として更新します。計算的には、サンプル データが特定のクラスに属する確率を、各クラスに属する確率の合計で割ったものです。たとえば、サンプルがクラス A に属する確率は 0.02、クラス B に属する確率は 0.001 です。この場合、サンプルがクラス A に属する可能性は (0.02/(0.02+0.001))*100 となり、約 95.23% になります。

対数確率:特定の属性値に対して、各クラスの条件付き確率は小さくなります。これらを掛け合わせると結果が小さくなるため、浮動小数点オーバーフロー(値が小さすぎて Python で表現できない)が発生する可能性があります。一般的な修正方法は、それらの確率の対数を組み合わせることです。この改善は研究され、実装することができます。

名詞属性:名詞属性をサポートするためにアルゴリズムの実装を改善します。これも非常によく似ており、各属性について収集する概要情報は、各クラスのクラス値の比率です。詳細については参考資料をご覧ください。

さまざまな密度関数 (ベルヌーイまたは多項式):ガウス ナイーブ ベイズを試しましたが、他の分布を試すこともできます。属性値の分布やクラス値との関係について異なる仮定を行う、多項式、ベルヌーイ、カーネル ナイーブ ベイズなどの異なる分布を実装します。

学習リソースと参考文献

このセクションでは、アルゴリズムの理論や仕組み、コード実装における実際的な問題など、Naive Bayes アルゴリズムについてさらに詳しく学習するためのリソースをいくつか提供します。

質問

糖尿病の問題を予測するためのさらなるリソース

  • ピマ族インディアン糖尿病データセット: このページではデータセット ファイルを提供し、さまざまなプロパティについて説明し、このデータセットを使用している論文を一覧表示します。
  • データセットファイル: データセットファイル
  • データセットの概要: データセット属性の説明
  • 糖尿病データセットの結果: このデータセットにおける多くの標準アルゴリズムの精度

コード

このセクションには、一般的な機械学習ライブラリにおける Naive Bayes のオープンソース実装が含まれています。実用化のために独自のバージョンを実装することを検討している場合は、これらを確認してください。

  • Scikit-Learn の Naive Bayes: scikit-learn ライブラリでの Naive Bayes の実装
  • Naive Bayes ドキュメント: scikit-learn ライブラリの Naive Bayes に関するドキュメントとサンプル コード
  • Weka でのシンプルな Naive Bayes: Weka での Naive Bayes の実装

応用機械学習に関する本をいくつか用意しておく必要があります。このセクションでは、人気の機械学習書籍から、Naive Bayes に関する章を紹介します。

  • 応用予測モデリング、353ページ
  • データマイニング: 実用的な機械学習ツールとテクニック、94 ページ
  • ハッカーのための機械学習、78ページ
  • 統計学習入門:R での応用、138 ページ
  • 機械学習:アルゴリズムの観点、171 ページ
  • 機械学習の実践、61 ページ (第 4 章)
  • 機械学習、177 ページ (第 6 章)

<<:  人工知能の歴史 - チューリングテストからビッグデータまで

>>:  滴滴出行の米国研究責任者:インテリジェント運転は間違いなく未来を変えるだろうが、そのプロセスは単純ではない

ブログ    

推薦する

人工知能によりデータセンターの設計が再考される

AI が企業で大規模に導入されるにつれて、データセンターのワークロードのより大きな割合が AI によ...

ワールドカップで物議を醸したVARテクノロジーはどのようにして生まれたのでしょうか?

Wiredウェブサイトは、FIFAの話題のVAR(ビデオ・アシスタント・レフェリー)の誕生過程を明...

馬毅教授の新作:ホワイトボックスViTが「セグメンテーション創発」に成功、経験的ディープラーニングの時代は終焉か?

トランスフォーマーベースのビジュアルベースモデルは、セグメンテーションや検出などのさまざまな下流タス...

10年前、古典的なword2vec論文が今日のNeurIPSタイムテスト賞を受賞しました

NeurIPS は世界で最も権威のある AI 学術会議の 1 つです。正式名称は Neural In...

自動運転によりシェアリングエコノミーは再び普及するでしょうか?

自動運転技術の開発は常に議論の的となっています。自動運転の将来に関して、避けて通れない話題が1つあり...

キャッシュに関して最も懸念される問題は何ですか?種類は何ですか?リサイクル戦略とアルゴリズム?

[[342437]]著者は、正確なタイミング タスクと遅延キュー処理機能を備えた、高同時実行シナリ...

マスク氏はテスラの完全自動運転が今年中に利用可能になると予測するが、AIの大きな変化を懸念している

同氏は、テスラは人間の介入なしの完全自動運転の実現に近づいていると述べ、完全自動運転の実用性と自動車...

...

マジック: メモリプーリングと分散 AI クラスターの最適化

[[429309]]分散機械学習が登場した理由は非常に単純です。一方では、トレーニングに利用できるデ...

人工知能技術の出発点と終着点

1. 人工知能技術の定義人工知能技術は、複雑な生産労働において機械が人間に取って代わることを可能にす...

スタンフォード大学の人工知能レポート: 今からでも遅くはない

スタンフォード大学は3月3日、2021年人工知能指数レポートを発表しました。その中で、AI関連の学習...

OpenAI API 高度な関数呼び出し実装プラグイン!

関数呼び出しの紹介関数呼び出しとは何ですか? OpenAI Chat API公式ドキュメント: Ch...

...