自然言語と人工言語のはざまで―ことばの研究・教育での言語処理技術の利用―(ことば文化特設サイト)
ことば文化に関する気になるトピックを短期連載で紹介していきます。
つづきは新書版の「リベラルアーツ コトバ 双書」にまとめる予定
-
- 2023年01月24日 『3. 自然言語と人工言語のはざまで―ことばの研究・教育での言語処理技術の利用―:CSVファイルとグラフによる言語データの可視化 野口大斗(東京医科歯科大学ほか非常勤講師)』
-
前回のコードは、最初のpタグのテキストのみではなくすべて取得するように修正しましたので、前回の部分のコードと実行結果が変わっています。
CSVファイルとグラフによる言語データの可視化
今回の流れ
今回は分析した結果を保存したり読み込んだりする方法、データの可視化、そして一連の作業をひとまとまりにする関数について紹介します。ファイルを扱う場合、Jupyter Notebookなどを使ってローカル(自分のコンピューター上)で作業をしていれば、ファイルのパスを指定するだけですが、Colaboratoryの場合にはGoogle Driveをマウントする作業を必要とします。まずは、CSVファイルから確認していきましょう。今回も入力済みのファイルを用意してあります。
CSVファイルとは
CSV(Comma-Separated Values)ファイルとはその名の通り、値(value)がコンマ(comma)で区切られた(separated)形式のテキストファイルです。表計算ソフトで開くと、Excelファイルと同じように値が表示されます。CSVファイルには値しか保存できませんが、値に関してはMicrosoft ExcelやGoogleスプレッドシートのような表計算ソフトでは同じように表示されます。メモ帳などのテキストエディタから開いてみると、エクセルファイルは人間には読めませんが、CSVファイルは表計算ソフトと同じ値がコンマで区切られているはずです。これがCSVの名前の由来です。それでは前回のコードを実行して、結果をCSVファイルとして保存してみましょう(1)。
前回のおさらい
前回のコードを実行してみましょう。このコードを実行することで、freqに頻度の情報が代入されます。
import requests
from bs4 import BeautifulSoup
import time
from collections import Counter
text = ''
base = 'https://en.wikipedia.org/wiki/'
items = ['Web_scraping', 'Robots.txt', 'Natural_language_processing']
for item in items:
webpage = requests.get(base+item)
soup = BeautifulSoup(webpage.content, 'html.parser')
p_tags = soup.find_all('p')
for p_tag in p_tags:
text = text + p_tag.get_text().replace('\n', ' ')
time.sleep(1)
text.lower().split()
freq = Counter(text.lower().split())
freq
Googleドライブのマウント
Googleドライブと連携させましょう。Google Colaboratoryの左端のバーにある、ファイルのマークのアイコンをクリックしてください。そうすると、開いた部分に4つのアイコンが出てきます。右から2つ目にある、カーソルをかざすと「ドライブをマウント」と表示されるものをクリックしてください。同意して自分のアカウントを選択し許可すると、ファイルに"drive"が追加されたことに気づくはずです。アイコンが見つからない場合は、つぎのコードをかわりに実行することもできます。これで、Googleドライブのマウントは完了です。
#Google Driveをマウントする。
from google.colab import drive
drive.mount('/content/drive')
データの保存
それでは、Googleドライブにデータを保存してみましょう。ここではpandasというライブラリーを使用して、freqの辞書データをdataframeという形式に変換してCSVファイルで保存します。まず、つぎのコードでデータの変換をおこないます。
#Pandasをインポートする。
import pandas as pd
#辞書データをdataframeに変換する。
df = pd.DataFrame.from_dict(freq, orient='index',columns=['frequency']).rename_axis('word')
df
データの数が多いので、20回以上出現する語に限定してみましょう。以下のように書きます。
#20回以上繰り返される語のみを抽出する。
df[df['frequency'] >= 20]
全体をCSVファイルで保存するには以下のようにします。ここでは、MyDriveに保存するようになっています。保存先を指定したい場合は、Colaboratoryのファイルマークのところで指定するフォルダーを選び、右の3つの点と「パスのコピー」を順にクリックします。パス(path)(2)とは道を意味する英単語で、フォルダーの階層構造がスラッシュでつながれたファイルの住所のようなものです。以下のコード中のパスは、contentの中のdriveの中のMyDriveの中にあるfrequencyという名前のcsvファイルということを示しています。コピーされているパスを貼りつけて、「/(ファイルの名前).csv」と追加すると指定先に保存が可能です。
#dataframeをcsvファイルでGoogle Driveに保存する。
df.to_csv('/content/drive/MyDrive/frequency.csv')
データの読み込みも簡単にできます。保存したばかりのファイルを、別の変数を使って読み込んでみましょう。もちろん保存先のパスから読み込むので、先ほど変更を加えた場合は、こちらも忘れずに変更してください。
#Google Driveのcsvファイルをdataframeとして読み込む。
df_1 = pd.read_csv('/content/drive/MyDrive/frequency.csv')
df_1
グラフの作成
通常、Pythonを用いてグラフを書く場合には、matplotlib(3)を使用します。しかし、pandasにはmatplotlibのラッパー(wrapper,包み込んで元とは違う環境で実行できるようにしたもの)があるため、簡単にグラフを描画することができます。今回は、棒グラフを作成してみましょう。スクレイピングした記事に20回以上出てくる語に限って可視化してみます。コードはたった1行でつぎのように書けます(4)。
#20回以上繰り返される語のみを抽出し、棒グラフにする。
df[df['frequency'] >= 20].plot(kind='bar')
頻度の多いものから順に並べることもできます。少ないものから並べる場合は、ascending=False
を削除するか、ascending=True
に書き換えます。
#20回以上繰り返される語のみを抽出し、降順に並べる。
df[df['frequency'] >= 20].sort_values('frequency', ascending=False)
プロットすると、以下のようになります。
#20回以上繰り返される語のみを抽出し、降順に並べて、棒グラフにする。
df[df['frequency'] >= 20].sort_values('frequency', ascending=False).plot(kind='bar')
これでグラフを作成することができました。plotのkindの値を変更するとグラフの種類を変更することもできます。また、凡例や軸についてはmatplotlibから設定できるので、興味があればウェブで検索して調べてみてください。
繰り返しを避ける(関数)
ここまでの手順をひとまとまり(関数)にして、繰り返しを避けましょう。まず、簡単な例として、与えられた数xに対して、3x^2+2x+1を計算してみましょう。例えば、[1,2,3]の場合はそれぞれつぎのようになります。
print(3*1**2+2*1+1)
print(3*2**2+2*2+1)
print(3*3**2+2*3+1)
なんだか打ち間違えそうです。高校の数学を思い出しましょう。関数を使って、以下のように表したはずです。(以下の関数は数学の例で、Pythonのコードではありません。)
f(x) = 3x^2+2x+1
f(1) = 6
f(2) = 17
f(3) = 34
これをPythonでコードにすると、つぎのようになります。defの後ろに関数名、かっこの中に変数(引数)を入れます。セミコロンとインデントを忘れないようにしましょう。関数の中身が複数行の場合は、すべてインデントします。今回は値を返したいので、returnを使用します。呼び出し方は、関数名のかっこの中に変数の値を代入するだけです。
#関数化する。
def f(x):
return 3*x**2+2*x+1
#関数に1から3までの値を代入する。
print(f(1))
print(f(2))
print(f(3))
それでは、本題に戻って、可視化の処理を関数にしてみましょう。
#スクレイピングから可視化までの流れを関数として定義する。
#itemにはリストでURLの末尾を、nには整数で単語の出現回数の閾値を受け取る。
def visualize(items, n):
import requests
from bs4 import BeautifulSoup
import time
from collections import Counter
import pandas as pd
text = ''
base = 'https://en.wikipedia.org/wiki/'
for item in items:
webpage = requests.get(base+item)
soup = BeautifulSoup(webpage.content, 'html.parser')
p_tags = soup.find_all('p')
for p_tag in p_tags:
text = text + p_tag.get_text().replace('\n', ' ')
time.sleep(1)
text.lower().split()
freq = Counter(text.lower().split())
df = pd.DataFrame.from_dict(freq, orient='index',columns=['frequency']).rename_axis('word')
df[df['frequency'] >= n].sort_values('frequency', ascending=False).plot(kind='bar')
このコードを一度実行しておくと、それ以降は簡単に呼び出すことができます。つぎのコードのように関数に引数を入れて呼び出すと、さきほどと同じグラフを描画できます。
#'Web_scraping', 'Robots.txt', 'Natural_language_processing'の記事で20回以上出てくる単語を棒グラフで可視化している。
visualize(['Web_scraping', 'Robots.txt', 'Natural_language_processing'], 20)
languageとEnglishの記事で40回以上出てくる語のように条件を変えても、短いコードで簡単に処理できます。
#'Language', 'English'の記事で40回以上出てくる単語を棒グラフで可視化している。
visualize(['Language', 'English'], 40)
さて、ファイルの読み込みと可視化、関数という今週の内容はおしまいです。ファイルを使えるようになるとできることの幅が大きく広がります。結果を保存できるだけでなく、既存のデータを読み込めるようになるからです。たとえば、最初に手作業で作成したCSVファイルも読み込むことができます。ファイルなしでは、単語の頻度や単語に含まれる文字数のカウントなどにできることが限られます。しかし、これからは既存のリストから語彙レベルを調べたり、既習語のリストを使って未習語を抽出したり、アイデア次第で挑戦できることが増えます。それらの一部については、この連載でも紹介予定です。
来週は正規表現を扱います。正規表現はあいまいな検索をする際に使用することができます。電子辞書やMicrosoft Wordなどで使用経験があるかもしれません。今回のグラフに問題があることに気づいた方も多いと思いますが、語形変化した単語が違うものとして扱われていて、冠詞や前置詞などの機能語が多く含まれています。正規表現を利用すると前者の問題を解決することができます。後者については、5週目以降に取り扱う予定です。それでは、来週の更新をお楽しみに!
注
(1) WindowsでShift-JISを使用している場合には、Excelなどで文字化けが起こることがあります。encodingを指定することも可能ですが、ファイルをUTF-8に統一することをおすすめします。比較的新しいExcelでは、保存の際にCSV UTF-8(コンマ区切り)を選べます。UTF-8のCSVファイルをExcelで読み込む際に文字化けが起こる場合は、「データ」-->「データの取得」-->「テキストまたはCSVから」として「65001: Unicode (UTF-8)」を選択して読み込むのがよいでしょう。
(2) Windowsを使用してJupyter Notebookなどでパスを指定する場合は、raw文字列の使用やエスケイプが必要になります。
(3) 日本語は文字化けする場合があります。japanize-matplotlibの使用を検討してみてください。
(4) 実行環境によっては、plt.show()が必要です。
-
- 2023年01月17日 『2. 自然言語と人工言語のはざまで―ことばの研究・教育での言語処理技術の利用―:Webテキストの利用(スクレイピング)と頻度のカウント 野口大斗(東京医科歯科大学ほか非常勤講師)』
-
Wikipedia記事の最初のpタグは本文の1段落目と一致しないことが多いため、pタグで囲まれたテキストの1つ目を取り出していたコードをすべて取り出すように書き換えました。(1/21)
Webテキストの利用(スクレイピング)と頻度のカウント
スクレイピングとその注意点
前回の内容はいかがだったでしょうか。基本的な話が中心だったため、あまり実践的ではなく、少し簡単だったかもしれません。今回はさっそく、すぐに活用できる技術についてお伝えします。前回は英文のテキストを手作業で貼りつけましたが、今回はウェブサイトから情報を抜き出す、スクレイピングという技術の紹介です。今回はURLが予測可能なタイプのウェブサイトの複数のページからテキストを収集し、それぞれの単語が出現する回数を数えることができるようになるということを目標に進めていきます。今回もPythonを使用します。
大まかな流れは、ウェブサイトのURLを生成する、URLを指定してページの情報を取得する、HTMLのタグを取り除く、単語の数を数えるというものになります。新たに紹介する主なこととしては、文字列の取り扱い、ライブラリーのインポート、HTMLの仕組みなどが挙げられます。
まずは、本題に入る前に注意点を確認しておきましょう。ウェブサイトをスクレイピングする際は利用規約を確認し、スクレイピングが許可されていても、サーバーに負担をかけないようにすることが重要です。サーバーへ高頻度のアクセスをすると、岡崎市立中央図書館事件(1)のように偽計業務妨害で罪に問われることがあります。スクレイピングの可否は、スクレイピングしたいウェブサイトのrobots.txtというページを確認します。今回はWikipedia(https://en.wikipedia.org)を使いますので、そのrobots.txtのページ(https://en.wikipedia.org/robots.txt)を確認します。本記事の執筆時点でWikipediaのrobots.txtは、"Friendly, low-speed bots are welcome viewing article pages"(友好的で低速であれば機械的なアクセスは歓迎される)となっています。ちなみにWikipediaで大量のテキストをダウンロードする場合は、全記事の専用ファイルが提供されている(2)ので、そちらを使うようにしましょう。
ウェブサイトの読み込み
実際にコードが動かないと面白くないので、説明は後回しにして、さっそくウェブページを読み込んでみましょう。Colaboratoryを開いてください。Google Driveで「新規」-->「その他」-->「Google Colaboratory」とクリック(「Google Colaboratory」がない場合は、「+アプリを追加」から追加)して開けます。前回と同じように、このリンク(https://colab.research.google.com/#create=true)を使っても構いません。今回も以下に入力済みのノートブックを用意しています。
つぎのコードを打ち込んでください。Wikipediaのウェブスクレイピングの記事(https://ja.wikipedia.org/wiki/ウェブスクレイピング)をスクレイピングしてみます。出力結果の見やすさのために日本語版を使用します。ひとまず、コードを実行してみましょう。
#requestsというライブラリーをインポートする。
import requests
#ウェブページを取得する。
#スペースの都合上、\を使用して折り返しているが、1行で書いても同じ。
webpage = requests.get('https://ja.wikipedia.org/wiki/'\
'%E3%82%A6%E3%82%A7%E3%83%96%E3%82%B9%E3%82%AF%E3%83%AC%'\
'E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0')
#テキストを表示する。
webpage.text
HTMLの処理
なんだか人間には読めそうにない文字列ではあるものの、ページの内容を取得できたことが確認できるはずです。少し見やすくして、本文だけを表示してみましょう。
#BeautifulSoupというライブラリーをインポートする。
from bs4 import BeautifulSoup
#HTMLを解析する。
soup = BeautifulSoup(webpage.content, 'html.parser')
#本文を抽出する。
soup.find_all('p')
改行記号とタグが入っているという問題点以外は、本文がうまく表示されているはずです。以下のようにすると、本文から改行記号とタグは削除されます。
#改行記号とタグを削除する。
for text in soup.find_all('p'):
print(text.get_text().replace('\n', ''))
HTMLの仕組み
さて、ページをうまく読み込めたので、少しHTMLの仕組みについて解説しておきます。HTML(HyperText Markup Language)とは、テキスト(text)よりも上位な(hyper)、しるしをつける(markup)ための言語(language)です。テキストをどう表示するのかのタグをつけるのに使われます。ウェブページは、実際に見えているように書かれているのではなく、テキストにそれぞれの部分をどう表示するかというタグ(<x>...</x>)がつけられています。実際はどのように記述されているのか確認してみましょう。さきほどのWikipediaのスクレイピングに関する記事を、Pythonではなく、ウェブブラウザから開いてみてください。
https://ja.wikipedia.org/wiki/ウェブスクレイピング
環境によって操作が異なるので、ここではGoogle Chrome(3)を使用している前提で話を進めます。さきほどPythonから読み込んだページをブラウザ(Google Chrome)から開きます。画面上を右クリックすると、「ページのソースを表示」と表示されるので、それをクリックします。ウェブサイトは、ソースに表示されているように記述されています。最初のコードで読み込んだものが読みにくかったのは、そのせいです。ブラウザがこのソースを解釈して、普段見ている形式で表示してくれているのです。
実際のウェブサイトは複雑でわかりにくいので、少し簡単な例で確認してみましょう。つぎの文字列をテキストエディタに貼り付けて、拡張子をhtmlとして保存してみましょう。ここでは、Windowsでメモ帳(テキストエディタ)を使っていることを想定して説明します。
<h1>見出し1</h1>
<h2>見出し2</h2>
<b>太字</b>
<i>斜体</i>
<u>下線</u>
まず、メモ帳を開きます。アプリが見つからない場合は、タスクバーの検索欄から検索してみましょう。メモ帳を開いたら、上記のタグで囲まれた文字列を貼りつけます。「ファイル」-->「名前を付けて保存」の順にクリック、ここでは仮に保存場所にデスクトップを指定します。適当な名前をつけ、その後ろに".html"と打ち込みます。たとえば、ファイル名を"test"とすると、以下のようになります。
test.html
ファイルの種類を「テキスト文書(*.txt)」から「すべてのファイル(*.*)」に変更して保存します。保存ができたら、デスクトップ(指定した保存場所)を確認してみてください。testのファイルが保存されているはずです。ファイルが既定のウェブブラウザのアイコンになっている場合は、そのままアイコンをダブルクリックして、ファイルを開いてみましょう。そうでない場合は、アイコンを右クリックして、「プログラムから開く」をクリックし、ウェブブラウザを選択してください。つぎのように、それぞれの「見出し」(headline)が大きく、「太字」が太字(bold)、「斜体」が斜体(italic)、「下線」が下線(underline)つきで表示されているのが確認できるはずです。実際のhtmlドキュメントや文字コードの宣言などを省略しているので、ブラウザなどの環境によっては、うまく動作しない可能性もあります(4)。
見出し1
見出し2
太字 斜体 下線
BeautifulSoupでのHTMLの処理の仕組み
最初のrequestsを使ったコードでは、webpage.textでHTMLタグを含んだままのテキストを表示していたので、人間には読みにくかったのです。ウェブサイトの本文だけを表示するコードでは、テキストを読みやすくするために、BeautifulSoupを使ってHTMLを解析することで、必要な部分だけを取り出しました。さきほど作ったHTMLの例で、動作を再度、確認してみましょう。
#test_htmlという変数に自作したhtmlを代入する。
test_html = '<h1>見出し1</h1><h2>見出し2</h2><b>太字</b><i>斜体</i><u>下線</u>'
#test_htmlを解析する。
soup = BeautifulSoup(test_html, 'html.parser')
#<h1>...</h1>とマークアップされている部分を探して、その部分のテキストを取得する。
soup.find('h1').get_text()
太字の部分を探したい場合は、h1をbに書き換えます。
#<b>... </b>とマークアップされているものをすべて取得して、1つ目を表示する。
soup.find_all('h1')[0].get_text()
URLの生成
さて、「URLが予測可能なタイプのウェブサイトの複数のページからテキストを収集し、それぞれの単語が出現する回数を数える」という目標を達成してみましょう。単語の数を数える際に、日本語を取り扱うと複雑になるので、今回も英語を使用します。Wikipediaの英文記事のURLを3つ用意してみました。
https://en.wikipedia.org/wiki/Web_scraping
https://en.wikipedia.org/wiki/Robots.txt
https://en.wikipedia.org/wiki/Natural_language_processing
まず、URLの構造は、簡単に予測できると思います。"https://en.wikipedia.org/wiki/"の部分が共通で、項目名の先頭が大文字になり、スペースが入る場合はアンダースコア(_)に置き換わっているというぐあいです。前回学んだことを使うと、以下のように項目名をリストにして、順にプリントすることができます。
items = ['Web_scraping', 'Robots.txt', 'Natural_language_processing']
for item in items:
print(item)
文字列動詞を結合するときには、足し算の記号を使います。
a = 'A'
b = 'B'
a + b
これを念頭におくと、URLはつぎのように生成できます。
base = 'https://en.wikipedia.org/wiki/'
items = ['Web_scraping', 'Robots.txt', 'Natural_language_processing']
for item in items:
print(base + item)
スクレイピングの実行
3つの記事の最初の段落の文章を結合してみましょう。
#必要なライブラリーをインポートします。
import requests
from bs4 import BeautifulSoup
import time
#空の文字列を用意しておきます。
text = ''
base = 'https://en.wikipedia.org/wiki/'
items = ['Web_scraping', 'Robots.txt', 'Natural_language_processing']
for item in items:
webpage = requests.get(base+item)
soup = BeautifulSoup(webpage.content, 'html.parser')
p_tags = soup.find_all('p')
for p_tag in p_tags:
text = text + p_tag.get_text().replace('\n', ' ')
#1秒間実行を停止させています。( )の中に停止させたい秒数を入れます。
time.sleep(1)
text
サーバーへの負担軽減
重要なのは、ループを回すごとに、動作を1秒間停止(sleep)させていることです(5)。sleepの動作についてだけ、簡単に確認しておきます。以下は、現在時刻を3回プリントするコードです。
#必要なライブラリーをインポートする。
import datetime
#3回ループさせるという意味である。
for i in range(3):
#現在時刻をプリントする。
print(datetime.datetime.now())
これにsleep(1)を挿入すると、1秒ずつ停止します。
import datetime
#timeというライブラリーも追加する。
import time
for i in range(3):
print(datetime.datetime.now())
#ループごとに1秒間停止させる。print文と字下げの深さがそろっていることに注意する。
#for文とそろえると、ループが終わってから1秒間停止する。
#そのため、アクセスの頻度を下げる役割を果たさなくなる。
time.sleep(1)
語の出現回数
さて、3つの記事を結合したtextに代入されているテキストに話を戻しましょう。前回のコードを使用すると、andが出てくる回数を数えることができます。
i = 0
for word in text.lower().split():
if word == 'and':
i = i+1
print(i)
今回は指定した文字だけではなく、すべての語の出現回数を求めようとしています。簡単な例['a', 'a', 'b']におけるそれぞれの文字の出現回数は、つぎのようにカウントできます(6)。
#ライブラリーをインポートする。
from collections import Counter
#リストの要素の出現回数をカウントする。
Counter(['a', 'a', 'b'])
textに代入されている文を前回と同じ要領でリスト化します。text.lower().split()
このリストにおける出現回数を数えるには、つぎのようにします。
from collections import Counter
Counter(text.lower().split())
変数に結果を代入しておきましょう。freq = Counter(text.lower().split())
すると、以下のようにすることで、先週のように任意の単語(ここではa)の出現回数を調べることができます。aの部分は調べたい単語で書き換えてみてください。freq['a']
これで、「URLが予測可能なタイプのウェブサイトの複数のページからテキストを収集し、それぞれの単語が出現する回数を数える」という目標は達成です。本当はそれぞれの単語を原形にしてカウントする必要があるのですが、それは今後の記事に譲ることにします。スクレイピングは実際に自分で選んだウェブサイトで試してみてください(7)。
実際にプログラムを使ってできそうなことのアイデアが、浮かんできたのではないでしょうか。たとえば、ページのソースでHTMLを確認すれば、ページ内の見出しだけを収集したり、リンクだけを収集してそのリンクのページをスクレイピングしたりすることなども可能です。プログラミングの入門書では、数値計算から始まるのがお約束ですので、2回目からスクレイピングというのは斬新だったかもしれません。しかし、コンピューター関連の情報は膨大ですので、すべて1度に学ぼうとするのではなく、まず興味のあるコードを動かしてみて、必要に応じて調べながら随時学んでいくのが、情報が目まぐるしく変化する社会では重要です。
また、プログラミングの面白いところは、小さな例から実験できる、何度やり直しても文句はいわれない、コピーさえとっておけば、自分のコンピューター内の状況は、いつでももとに戻すことができるということです。そして、自分の頭の中にあるものを実際の形にすることができます。失敗をおそれずに、どんどんコードを書き換えてみてください。うまく動かなくなったら、このページから再度コードを取得すれば問題ありません。今回のスクリプトでは、ウェブサイトにアクセスします(自分の外の世界に影響を与えます)ので、その時だけ注意を払ってください。そうはいっても、必要なのは、アクセスしてよいサイトか確認をすること(ブログラムが書けるのかどうかに関係がない能力)と、早くアクセスしすぎていると感じたらPythonを終了する、インターネットから切断する、コンピューターをシャットダウンしてやり直すといった基本的な対応だけです。
次回は単語の頻度をグラフにしてみます。Google Driveと接続して、結果をファイルで書き出す方法や同じコードの繰り返しを避ける方法(関数)などについても紹介します。それでは、来週の更新をお楽しみに!
注
(1) https://ja.wikipedia.org/wiki/岡崎市立中央図書館事件
(2) https://ja.wikipedia.org/wiki/Wikipedia:データベースダウンロード
(3) https://www.google.com/chrome
(4) うまく動作しない場合は、以下のものを試してみてください。
(5) robots.txtに“Hits many times per second, not acceptable”(1秒間に何度もアクセスするのは許されない)とあるので、1秒に1回に制限しています。アクセス先のウェブサイトに合わせて、調節してください。<!DOCTYPE html>
<html lang=”ja”>
<head>
<meta charset="UTF-8">
<title>My test page</title>
</head>
<body>
<h1>見出し1</h1>
<h2>見出し2</h2>
<p><b>太字</b><i>斜体</i><u>下線</u></p>
</body>
</html>
(6) 本文中では省きましたが、辞書というタイプのデータは、つぎのように使用できます。
また、コードを単純にするためにライブラリーを使用しましたが、単語のカウントは辞書を用いて、以下のようにもおこなえます。#空の辞書を用意する。
dictionary = {}
#appleという見出しの値を1にする。
dictionary['apple'] = 1
#現状のdictionaryの中身をプリントする。
print(dictionary)
#bananaという見出しの値を2にする。
dictionary['banana'] = 3
#最終的なdictionaryの中身をプリントする。
print(dictionary)freq_letter = {}
for letter in ['a', 'a', 'b']:
if letter not in freq_letter:
freq_letter[letter] = 1
else:
freq_letter[letter] = freq_letter[letter]+1
freq_letter
(7) JavaScriptが使用されているページをスクレイピングする際には、seleniumなどのライブラリーが必要になります。JavaScriptが使用されているか確認するには、Google Chromeで右上の3つの点の部分をクリックし、「設定」-->「プライバシーとセキュリティ」-->「サイトの設定」-->「JavaScript」-->「サイトに JavaScript の使用を許可しない」を選択し、ページを再読み込みしましょう。この設定で閲覧できなくなった部分に関しては、JavaScriptが使われています。多くのウェブサイトが正常に動作しなくなるため、確認が終わった後は、「サイトが JavaScript を使用できるようにする」に戻し、ページを再読み込みしてください。
-
- 2023年01月10日 『1. 自然言語と人工言語のはざまで―ことばの研究・教育での言語処理技術の利用―:ことばを取り巻く環境とPythonの動作確認 野口大斗(東京医科歯科大学ほか非常勤講師)』
-
自然言語と人工言語のはざまで
この連載では、言語の研究と教育におけるテクノロジーの活用について扱います。自然言語(人間のことば)に興味のある方、人工言語(プログラミング言語)を学び始めた方を念頭に執筆しています。具体的には、プログラミングを学び始めた中高生、日本語(国語)や英語を教えている方、情報の授業のプログラミングの題材を探している中高の教員、プログラミングを活用したい言語の研究に関わっている学生や研究者、ことばに関心がありプログラミングを始めてみたい方を想定しています。プログラミングや自然言語処理の知識のある方には十分な内容となっていないことをあらかじめお断りいたします。初回ですので、わたしたちのことばを取り巻く環境についてすこし前置きをしてから、本題に入りたいと思います。
(本連載では、主にPythonを利用します。Googleのアカウントを持っていることを前提として話を進めますので、アカウントがない場合にはぜひ新規作成してコードを実行してみてください。コードは、Windows 11上のGoogle Chromeでの動作を確認しています。コードの確認には万全を期していますが、利用結果にともなう責任は負いません。)
ことばを取り巻く環境
ことばのゆくえ
まず、外国語と関連するものとして、機械翻訳(Google翻訳やDeepLなど)は、多くの読者のみなさんにとって親近感があるのではないでしょうか。意識的に使っていなくても、ブラウザやサイトの設定で、ウェブページが自動的に翻訳されることさえあります。音声認識の精度も向上し、YouTubeやPowerPointの自動生成の字幕や同時翻訳の字幕を見たことがある方も多いはずです。文字認識技術の発展も驚異的で、タイプされた文字であれば、ほぼ誤りなく認識され、Google Lensなどはカメラをかざすだけで、即座に文字を翻訳してくれます。そんなものは使ったことがないと言われる方も、街中で知らず知らずのうちに、翻訳された文やそれが読み上げられた合成音声を、目にしたり耳にしたりすることくらいはあるはずです。それほどに、言語処理の技術はわたしたちの生活に浸透しています。
言語処理の技術は、外国語に限ったことではありません。母語であったとしても(わたしの場合は日本語ですが)、知らず知らずのうちに、わたしたちはテクノロジーに依存しています。たとえば、この原稿をタイプしながら、かな漢字変換システムを利用しています。現在では、文節ごとに変換して候補を吟味しなくても、うまくいくことが多いですし、個人の変換傾向に合わせたカスタマイズまでされるようになっています。メモ帳のようなテキストエディタではなく、高級なアプリケーションを使っていると、文章校正までおこなわれ、修正候補が提示され、設定によっては自動修正までおこなわれます。スマートフォンやタブレットでは、予測候補が表示され、もはや自分で文を紡ぎ出しているのかどうかさえ怪しくなってきているのが、昨今の状況です。
さらに、2022年の年末にChatGPTが話題になったことは、記憶に新しいかもしれません(https://chat.openai.com)。システムがことばを自動的に生成することもできます。機械が人間と対話してタスクをこなすということも驚くべきことです。通常の作文だけではなく、歌詞や参考文献を含むレポートを書いたり、意図的に間違いをくわえたりすることさえ、お手のものです。人間に特有であったはずの能力―ことば―は、いったいどこへいってしまうのでしょう。
ひとつの答え―積極的な活用
あまり悲観的になる必要はないのではないでしょうか。少なくとももうしばらくのあいだは、人間はコミュニケーションに言語を使いつづけるでしょうから、根本的に言語が人間の手を離れることはないと想像されます。また、機械学習に使われるデータは基本的にこれまでに人間が蓄積してきたものであるため、機械の学習システムは人間のそれと大きく変わらないはずです。わたしがいま書いている文も、どこかで誰かが使ったのを読んだり聞いたりしたことのある構文と語の可能な組み合わせのうちの1つにしかすぎないのと同じです。
ただし、人間と機械の大きな違いは、機械は人間が死ぬまでかかっても読み切れないほどのデータを、学習していることです。そして、機械は学んだことを忘れません。したがって、1人の人間よりも能力が高いのは、当然のことなのです。目くじらをたてることはありません。歴史に名を残すような偉人を除いて、多くの人間の日常の営み(少なくとも言語活動)はさほど創造的ではなく、機械に模倣されるということが明らかになっただけなのです。
では、わたしたちにできることは何なのでしょうか。わたしにとって、この問題へのアプローチは極めてシンプルです。積極的にテクノロジーを活用するのです。日常生活を思い浮かべてみてください。文書はコピー機を使わずに手で写すべきだ、車のギアはATではなくてMTでないといけない、単語の意味は紙の辞書で調べないといけないと主張するのは少数派でしょう。しかし、少し話をハイテクにすればどうでしょうか。書類ではなくてフォームで電子データにすべきだ、運転は自動化すべきだ、日常的に使わないのであれば実用的には外国語は機械翻訳で十分だ。これらについては、まだ意見がわかれるのではないでしょうか。
自動化の誤解
これらに共通するのは、人間のやることが簡単になるのではなく、まったくなくなるということです。事務、運転、外国語の仕事がなくなるからという抵抗にあって、自動化は前に進みにくいのです。(誤解のないように書いておくと、わたしは収入の大部分を外国語教員という職に依存していますし、実用性だけが外国語の効用だとは思いません。)そうだとしても、自動化できることを自動化するのはよいことのはずです。電話交換手やタイピストが必要だという意見は少ないでしょう。時代とともに、人間がやらないといけないことは減ってきています。
もちろん、自動化されて仕事から解放されるのは、悪いことではありません。それで生活できなくなることが問題なのです。たとえば、自宅の掃除が自動化されても、だれも文句は言いません。むしろ対価を払ってでも、ルンバなどの掃除ロボットを買い求める人もいます。家事に収入が発生しないからです。自動化と収入の問題は、切り分けて議論しなくてはならないのです。
テクノロジーの可能性
そうであれば、言語の処理の一部を選択的に自動化することはよいことのはずです。世の中に存在するテキストの量は、確実に増え続けていきます。すでに、人間がすべてを読むことは不可能です。検索や要約の処理ができるのはすばらしいことです。人間が生涯に学習できる言語の数にも限りがあります。機械翻訳の進歩によって、読めるテキストの範囲が増えるのも望ましいことです。機械で文章を自動生成できることが広く知られたら、長く書くことの意義も薄れ、文書作成からも解放されるでしょう。
ただし、既存の提供されているシステムだけでできることには限りがあります。研究や教育にぴったり合ったプログラムはないか、あっても有償であることがほとんどです。テクノロジーを最大限に活用するには、コードの仕組みを知っている必要があります。ChatGPTなどで自然言語からコードを生成することも可能になっているので、データを整形したり、スクリプトを組み合わせたりすることさえできれば、自然言語処理技術の活用の幅は大きく広がるはずです。言語処理の技術を開発するのは容易なことではありませんが、それを言語の研究や教育に利用することはそれほど難しいことではありません。それでは、全12回の連載で、自然言語と人工言語のはざまをさまよう旅にご案内します。ぜひ最後までおつき合いください。
Pythonの動作確認
Google Colaboratory
今回は練習として、テキストに特定の単語が現れる回数を数えてみます。日本語も取り上げる予定ですが、しばらくはプログラム上での扱いやすさのために、英語の例を使用します。主にPythonというプログラミング言語を使いますが、環境設定の手間を省くため、この連載ではGoogle Colaboratoryを使用します。出力結果は表示しませんので、実際に実行して確認してください。コードはGoogle側で実行されるため、コンピューターへの負荷はかかりません。スペックを気にする必要がないことも、Google Colaboratoryを使う利点です。
準備は、以下のリンクをクリックして、Googleにログインするだけです。すぐにコードを打ち込めるようになります。ファイルはColab Notebooksというドライブ上のフォルダー内に保存されます。
https://colab.research.google.com/#create=true
まずは、実際にこのページで紹介しているコード(字体の変わっている部分)を打ち込んでみることをおすすめしますが、以下のすでに入力済みのものからすぐに実行することもできます。実行する際に警告画面が出ます。また、1つ目のコードの実行には、接続のために少し時間がかかります。スマートフォンからでも実行できます。再生(実行)ボタンはカーソルをかざさないと出てこないため、スマートフォンの場合はコードの横の[ ]の部分をタップしてください。こちらからの実行内容は保存されません。保存したい場合は、「ファイル」-->「ドライブにコピーを保存」と操作して、コピーしたものを使用してください。
テキストの読み込み
テキストはファイルから読み込むのが理想的ですが、ColaboratoryにGoogle Driveをマウントする必要があります。また何らかのデータセットから、テキストをインポートする魔法の呪文を提供することも可能です。しかし、わかりやすさのために、今回はコードにテキストを直接貼りつけます。また、コードの簡潔さのために説明を省いたり、お行儀の悪いコーディングをしたりします(1)が、ご了承ください。コードはセルという枠の中に、以下のように記述します。コード内の#に続く、もしくは3つのシングルクオーテーションで囲まれた日本語の文字列はコメントのため不要です。また、コメント以外の部分に全角文字を入れるとエラーが出るので、注意してください。
'''テキストに説明文を代入する。
イコールの前後のスペースはあってもなくてもよい。'''
text = 'Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Python’s elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many areas on most platforms.'
ここでは、textという変数に、Pythonのチュートリアル(2)の説明文の1段落目を代入します。コードはできるだけ、コピーではなく打ち込んでみてください。(もちろんチュートリアルの説明文はコピーで構いません。)文字列としてチュートリアルの説明文を代入したいので、引用符を忘れないようにしてください。Pythonではシングルクオーテーションでもダブルクオーテーションでも問題ありません。コードの左の三角形の再生ボタンを押して、セルを実行してみましょう。これで代入が完了です。
つぎに、「+コード」を押して、新たなセルを作り、次のコードを打ち込んで、セルを実行してください。「+テキスト」ではないことに注意してください。「テキスト」は「コード」以外の情報を書き込む部分です。textに文字列が代入されていることを確認できます(3)。
#textの中身をプリント(表示)する。
text
単語への分割
つぎにコードを少し変えて、実行してみましょう。
#textを小文字にする。
text.lower()
これで、textに代入されている文字すべてが小文字(lower-case letters)になります。大文字で書かれている場合も小文字で書かれている場合も同じ単語として扱いたいので、この処理を施します。
さらに変更して、単語ごとに分割(split)してみましょう。
#textを小文字にして分割する。
text.lower().split()
リストという四角いかっこで囲まれてコンマで区切られたものが出力されます。ピリオドやコンマが不要ですが、未習事項(4)を使わないと解決できないので無視します。
ループ
繰り返しの操作について確認しましょう。以下の例では、リストの中のものを順に取り出して、プリントしています。コロンを忘れないようにしてください。Pythonでは字下げの位置関係が、コードの階層構造を示します。上から入力すると自動で字下げしてくれますが、Tabキーで字下げし、Shiftキー+Tabキーで字下げを元に戻すこともできます。
'''inの後ろのリストの要素の数の分だけループを回し、
リストの要素を順にforの後ろのwordに代入する。
wordはほかの変数名に変えてもよいが、
その場合は後続する変数も忘れずに変更する。'''
for word in ['a', 'b', 'c']:
#wordをプリントする。
print(word)
さきほどの単語のケースに置き換えると、ループを使ったコードはつぎのようになります。
#小文字化されたテキストのそれぞれの単語をプリントする。
for word in text.lower().split():
print(word)
条件分岐
簡単な例を使って、bだけをプリントする例を示します。今回はifが入れ子になったので、さらに字下げをします。代入はイコール1つですが、等しいかどうかの判定は2つになるので注意してください。
#リストのそれぞれの要素について同じ処理を繰り返す。
for word in ['a', 'b', 'c']:
#もしwordに入っている要素がbであれば、以下の処理を実行する。
if word == 'b':
#wordの中身をプリントする。
print(word)
実際の単語のケースで、さきほどのコードの判定する要素をandに置き換えると、つぎのようになります。
#小文字化された単語それぞれについて同じ処理を繰り返す。
for word in text.lower().split():
#もしwordに入っている単語がandであれば、以下の処理を実行する。
if word == 'and':
#wordの中身をプリントする。
print(word)
カウント
andが出現する回数を数えてみましょう。iという変数を用意して、0を入れておきます。andが見つかった場合に、iに1をくわえていくことにしましょう。
#iという変数に0をセットする。
#counterなど別の名前にしてもよい。
i = 0
for word in text.lower().split():
if word == 'and':
#単語がwordであった場合だけに1を追加する。
i = i+1
#複数の要素をプリントしたい場合は、コンマでつなぐ。
print(i, word)
andの出現回数だけ表示したい場合は、つぎのようにします。print文の字下げの深さが変わっているのに注意してください。
i = 0
for word in text.lower().split():
if word == 'and':
i = i+1
'''さきほどとは字下げの位置が変わり、
すべての繰り返しが終わってからiをプリントしていることに注意する。'''
print(i)
さて、長くなりましたが、今回はこのあたりでおしまいにしましょう。次回は指定したURLからウェブ上のテキストを自動で取り出して、それぞれの単語が出現する回数を数えてみることにします。ウェブからテキストを取り出す技術は、情報収集や自らコーパスを作成する際に活用可能です。連載の中盤では、語数の数え方を応用して、自分で指定した英文から自分の知っている単語を除いた、オリジナルの英単語帳を作るコードを紹介します。テキストの難易度を評価したり、未習語を洗い出したりする作業にも用いることができます。それでは、来週の更新をお楽しみに!
注
(1) たとえば、変数を極端に減らしたり、メソッドや副作用の説明を省略したりしています。Pythonについて興味がある場合は、入門の書籍やウェブサイト、動画、公式チュートリアルなどを参照することを推奨します。
(2) https://docs.python.org/3/tutorial
(3) ColaboratoryやJupyter Notebookではprint文がなくてもprintされるので省きますが、実行環境によってはprint(text)
としてください。
(4) リストをループで一度展開して、stripを使用したものを別のリストにappendするか、つぎのようにリスト内包表記を利用することができます。[word.strip('.').strip(',') for word in text.lower().split()]
-
- 2023年01月03日 『1月からの新連載のお知らせ』
-
10月から12回にわたり、「中国のことばの森の中で―武漢・上海・東京で考えた社会言語学」というタイトルで、新型コロナウイルス感染症の拡大で近くて遠い国になってしまった中国を社会言語学の観点から描いてくださった河崎みゆき先生、ありがとうございました。目の前に街並みが浮かんでくるような筆致で中国のことばの森を描写してくださいました。
さて、1月10日からの12回の連載は野口大斗(東京医科歯科大学ほか非常勤講師)による「自然言語と人工言語のはざまで―ことばの研究・教育での言語処理技術の利用―」です。言語の音やテクノロジーを活用した言語の研究や教育に関心があります。この連載では、Pythonなどで言語処理の技術を研究や教育にどのように利用できるかについてみなさんと考えていきます。(野口)