試行せずに人種差別的なAIを作成する方法。 パート1

先日、音声認識における人種差別の問題に焦点を当てた別の記事に基づいて、私は誰が責任を負うべきかについての大きな議論に参加しました。 一部の人々は、これがプログラマの陰謀であると確信していました。 実際、真実はAIがトレーニングに使用するデータにあります。 これを明確に実証する実験を行うことにしました。 Rob Speerがすでに私のためにすべてを行っていたことが判明しました。



彼の資料の翻訳を皆さんと共有したいと思います。これは、AIの最もデフォルトのバージョンでさえ、人種差別で完全に飽和することを明確に示しています。 最初の記事では実験を行い、次の記事では生成したモンスターを克服する方法を見つけようとします。







たぶん、MicrosoftがTwitterで立ち上げたTayの実験的なチャットボットについて聞いたことがあるかもしれません。 わずか1日で、彼のメモは非常に挑発的なものになり、Microsoftはボットを無効にしなければならず、二度と彼の名前を口に出さなかった。 おそらく、あなたは奇妙なことをしていないので、これはあなたを脅かすとは思わないでしょう(特に、ローファーにTwitterに基づいてAIを訓練する機会を与えていない)。



このガイドでは、次のことを示したいと思います。最も標準的な自然言語処理アルゴリズム、一般的なデータセットおよびメソッドを使用しても、結果は人種差別主義の分類器であり、自然には存在しないはずです。



朗報:これは回避できます。 分類器で人種差別的な習慣の出現を排除するには、少し余分な努力をする必要があります。 この場合、修正されたバージョンはさらに正確になる可能性があります。 しかし、問題を解決するには、それが何であるかを知る必要があり、最初の有効なオプションをつかむ必要はありません。



テキスト分類子を作成しましょう!



感情分析は非常に一般的なNLPタスクであり、驚くことではありません。 人が肯定的または否定的なコメントを残したかどうかを理解できるシステムは、ビジネスで多くの用途があります。 このようなソリューションは、ソーシャルネットワーク上の出版物の監視、顧客レビューの追跡、さらには証券取引( たとえば 、女優のアンハサウェイが批評家から良いレビューを受けた後にバークシャーハサウェイの株式を購入したボット)にも使用されます。



これは、単純化された(場合によってはあまりにも単純すぎる)アプローチですが、人々が作成したテキストの定量的な見積もりを取得する最も簡単な方法の1つです。 ほんの数ステップで、テキストを処理し、プラスとマイナスの評価を発行するシステムを準備できます。 この場合、解析ツリーやエンティティダイアグラムなどの複雑なデータ表現形式を扱う必要はありません。



次に、NLPの専門家に馴染みのある分類器を作成します。 同時に、各段階で実装する最も簡単なオプションを選択します。 このようなモデルは、たとえば、記事Deep Averaging Networksで説明されています。 それは記事の主要な主題ではないため、この言及は得られた結果に対する批判と見なされるべきではありません。 そこでは、モデルは単語のベクトル表現を使用するよく知られた方法の例として単に提示されています。



アクションプランは次のとおりです。





その後、人種差別的なAIを意図せずに作成する方法がわかります。



このようなフィナーレを避けたいので、次のことを行います





ソフトウェア前提条件



このガイドはPythonで書かれており、すべてのライブラリは以下にリストされています。



import numpy as np import pandas as pd import matplotlib import seaborn import re import statsmodels.formula.api from sklearn.linear_model import SGDClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score %matplotlib inline seaborn.set_context('notebook', rc={'figure.figsize': (10, 6)}, font_scale=1.5)
      
      





scikit-learnは、TensorFlow、Keras、または勾配降下アルゴリズムを含む他のコンポーネントに置き換えることができます。



ステップ1.単語のベクトル表現



単語のベクトル表現は、多くの場合、単語を機械学習システムで処理しやすい形式に変換するために使用されます。 単語は多次元空間でベクトルとして表されます。 ベクトル間の距離が小さいほど、対応する単語の意味が近くなります。 単語のベクトル表現を使用すると、単語を文字ごとではなく、(おおよその)意味ごとに比較できます。



単語の適切なベクトル表現を取得するには、数百ギガバイトのテキストを処理する必要があります。 幸いなことに、機械学習の専門家の多くのグループがすでにこの作業を行っており、完成した教材を共有しています。



単語のベクトル表現には2つの有名なセットがあります:word2vec(Google Newsのデータは作成の教材として使用されました)とGloVe(教育資料:Common Crawlによって処理されるWebページ)。 最終結果は両方のセットで類似しています。 GloVeはより透明なデータソースに基づいているため、これを使用します。



3つのGloVeアーカイブがダウンロード可能です:6、42、840億レコード。 840億は膨大ですが、このアーカイブから420億番目のセットよりも多くの利点を引き出すには、複雑な後処理が必要になります。 420億のバージョンは非常に機能的であり、100万というラウンド数の単語が含まれています。 抵抗が最も少ない経路にあるため、420億バージョンを使用します。



そのため、GloVe Webサイトからglove.42B.300d.zip



アーカイブをダウンロードし、 data/glove.42B.300d.txt



を解凍しdata/glove.42B.300d.txt



。 次に、単純な形式で単語​​のベクトル表現を読み取る関数を作成する必要があります。



 def load_embeddings(filename): """ Load a DataFrame from the generalized text format used by word2vec, GloVe, fastText, and ConceptNet Numberbatch. The main point where they differ is whether there is an initial line with the dimensions of the matrix. """ labels = [] rows = [] with open(filename, encoding='utf-8') as infile: for i, line in enumerate(infile): items = line.rstrip().split(' ') if len(items) == 2: # This is a header row giving the shape of the matrix continue labels.append(items[0]) values = np.array([float(x) for x in items[1:]], 'f') rows.append(values) arr = np.vstack(rows) return pd.DataFrame(arr, index=labels, dtype='f') embeddings = load_embeddings('data/glove.42B.300d.txt') embeddings.shape # (1917494, 300)
      
      





ステップ2.キーの標準辞書



どこかから、どの単語がポジティブなトーンを持ち、どの単語がネガティブなトーンを持つかについての情報を取得する必要があります。 調性には多くの語彙がありますが、通常どおり、最も単純なものを選択します。 Bin LiuのWebサイトからアーカイブをダウンロードし、辞書ファイルdata/positive-words.txt



およびdata/negative-words.txt



ます。



次に、これらのファイルを読み取り、その内容をpos_words



およびneg_words



に読み取る方法を指定する必要がありneg_words







 def load_lexicon(filename): """ Load a file from Bing Liu's sentiment lexicon (https://www.cs.uic.edu/~liub/FBS/sentiment-analysis.html), containing English words in Latin-1 encoding. One file contains a list of positive words, and the other contains a list of negative words. The files contain comment lines starting with ';' and blank lines, which should be skipped. """ lexicon = [] with open(filename, encoding='latin-1') as infile: for line in infile: line = line.rstrip() if line and not line.startswith(';'): lexicon.append(line) return lexicon pos_words = load_lexicon('data/positive-words.txt') neg_words = load_lexicon('data/negative-words.txt')
      
      





ステップ3.単語の調性を予測するためのモデルのトレーニング



GloVe辞書に一部の単語がありません。 ベクトル値がない場合、読み取りの結果としてNaN値からベクトルを取得します。 そのようなベクトルを削除します。



 pos_vectors = embeddings.loc[pos_words].dropna() neg_vectors = embeddings.loc[neg_words].dropna()
      
      





次に、目的の入力データと出力データの配列を作成します。 入力データ:単語のベクトルの意味。 週末:正の色の単語の場合は値1、負の色の単語の場合は-1。 また、結果を解釈できるように、単語自体を保存する必要があります。



 vectors = pd.concat([pos_vectors, neg_vectors]) targets = np.array([1 for entry in pos_vectors.index] + [-1 for entry in neg_vectors.index]) labels = list(pos_vectors.index) + list(neg_vectors.index)
      
      





ちょっと! しかし、中立的な言葉もありますが、調性はありません。 中立的な単語には3年生が必要ではないでしょうか?



私たちが遭遇する問題は、中性語への調性の帰属のために発生するので、中性語の例は私たちにとって有用だと思います。 中立的な単語を確実に識別できれば、分類子の複雑化(3番目のクラスの追加)が正当化されます。 これを行うには、中立的な単語の例のソースが必要です。選択したセットでは、正と負の色の単語のみが表示されるためです。



そのため、このノートブックの別バージョンを作成し、800個の中立単語を例として追加し、単語の中立性に大きな重み係数を設定しました。 しかし、結果は以下に示すものとほとんど同じでした。



リストの作成者は、正と負の調性の単語をどのように分離しましたか? 調性は文脈に依存しませんか?



いい質問です。 テキストの調性の一般的な分析は、見かけほど単純な作業ではありません。 私たちが見つけようとしている境界線は、必ずしも明確ではありません。 選択したリストでは、「厚かましい」という言葉は悪いとマークされ、「野心的な」という言葉は良いとマークされています。 「コミック」は悪い、「面白い」は良い。 あなたまたはあなたが補償を必要とする状況はめったに快適ではありませんが、「補償」は良いです。



単語の調性は文脈に依存することは誰もが理解していると思いますが、調性の分析に単純なアプローチを実装する場合、単語の調性の平均値は文脈を考慮せずに正解全体を得ることができると仮定します。



入力ベクトル、出力値、およびラベルをトレーニングデータセットとテストデータセットに分割します。 テストには、データの10%を使用します。



 train_vectors, test_vectors, train_targets, test_targets, train_labels, test_labels = \ train_test_split(vectors, targets, labels, test_size=0.1, random_state=0)
      
      





次に、分類子を作成し、そのためのトレーニングを開始します-トレーニングベクトルの処理を100回繰り返します。 損失の関数として、ロジスティック関数を使用します。 したがって、分類器は、指定された単語が正または負の色である確率を計算できます。



 model = SGDClassifier(loss='log', random_state=0, n_iter=100) model.fit(train_vectors, train_targets)
      
      





次に、テストベクトルの分類子を確認します。 彼は、95%のケースでトレーニングセット外の単語の調性を正しく認識していることがわかりました。 全然悪くない。



 accuracy_score(model.predict(test_vectors), test_targets) # 0,95022624434389136
      
      





また、分類器によって予測された個々の単語の調性を示す関数を定義します。 分類器は、トレーニングセットに含まれていない単語の調性を評価できます。



 def vecs_to_sentiment(vecs): # predict_log_proba gives the log probability for each class predictions = model.predict_log_proba(vecs) # To see an overall positive vs. negative classification in one number, # we take the log probability of positive sentiment minus the log # probability of negative sentiment. return predictions[:, 1] - predictions[:, 0] def words_to_sentiment(words): vecs = embeddings.loc[words].dropna() log_odds = vecs_to_sentiment(vecs) return pd.DataFrame({'sentiment': log_odds}, index=vecs.index)
      
      





ステップ4.テキストのセンチメントスコアを取得する



個々の単語のベクトル表現の調性値に基づいてテキストの調性を評価する方法は多数あります。 私たちは、最小の抵抗の道をたどり、それらを単純に平均し続けます。



 import re TOKEN_RE = re.compile(r"\w.*?\b") # The regex above finds tokens that start with a word-like character (\w), and continues # matching characters (.+?) until the next word break (\b). It's a relatively simple # expression that manages to extract something very much like words from text. def text_to_sentiment(text): tokens = [token.casefold() for token in TOKEN_RE.findall(text)] sentiments = words_to_sentiment(tokens) return sentiments['sentiment'].mean()
      
      





ここで何を改善できますか?





しかし、これらすべてのために、追加のコードを書く必要があり、以下の結果は根本的に変わりません。 少なくとも、異なる文の相対的な感情的な色を大まかに比較することができます。



 text_to_sentiment("this example is pretty cool") # 3.889968926086298 text_to_sentiment("this example is okay") # 2.7997773492425186 text_to_sentiment("meh, this example sucks") # -1.1774475917460698
      
      





ステップ5.作成したモンスターを怖がらせる



一部の文には、明確な口調の単語は含まれません。 私たちのシステムが同じニュートラルオファーのいくつかの類似物を処理する方法を見てみましょう。



 text_to_sentiment("Let's go get Italian food") # 2.0429166109408983 text_to_sentiment("Let's go get Chinese food") # 1.4094033658140972 text_to_sentiment("Let's go get Mexican food") # 0.38801985560121732
      
      





同じことは、単語のベクトルの意味を使用してレストランのレビューを分析した他の実験でも起こりました。 その後、メキシコのすべてのレストランは客観的な理由なしに調性の低い評価を受けることが判明しました。



コンテキストに従って単語を処理する場合、単語のベクトルの意味は、意味の微妙なニュアンスを反映できます。 これは、社会的偏見など、より顕著な現象を検出できることを意味します。



ここにいくつかの中立的な提案があります。



 text_to_sentiment("My name is Emily") # 2.2286179364745311 text_to_sentiment("My name is Heather") # 1.3976291151079159 text_to_sentiment("My name is Yvette") # 0.98463802132985556 text_to_sentiment("My name is Shaniqua") # -0.47048131775890656
      
      





まあまあ。



名前を変更するだけで、システムが与える調性の評価が大きく変わります。 この例および他の多くの例は、白人に関連付けられている名前を使用する場合、肌の色が暗い人にステレオタイプの名前がある場合よりも、予測される調性が平均して正であることを示しています。



そのため、AIの最も基本的な実装であってもひどく偏っていることを確認して、反射のために少し休止することを提案します。 2番目の記事では、このトピックに戻り、意図しないAIのエラーを修正します。



All Articles