疑念は探究の動機であり、探究の唯一の目的は信念の確定である。

数学・論理学・哲学・語学のことを書きたいと思います。どんなことでも何かコメントいただけるとうれしいです。特に、勉学のことで間違いなどあったらご指摘いただけると幸いです。 よろしくお願いします。くりぃむのラジオを聴くこととパワポケ2と日向坂46が人生の唯一の楽しみです。

ChatGPT-4にウィノグラードスキーマ問題を試してみた

概要

コンピュータが自然言語の意味を理解しているのかをテストするウィノグラードスキーマ問題というのがある。2019年時点では250問のウィノグラードスキーマ問題に対して、その正解率は最大で約61%だった。

今回ChatGPT-4で試したら91.5%(正解数 250問/総数 273問)であった。今回は英語版でおこなったが日本語ではどのような結果が出るのか気になった。いずれにせよAIの進歩は凄まじいと思った。




プログラム

フォルダ構成

.
├── winograd_schema_challenge.csv
└── winogradschemachallenge
    ├── .env
    └── test_winograd_schema.py


ソースコード

  • .env
OPENAI_API_KEY='sk-XXXXXXXXXXXXXXXXX'


  • test_winograd_schema.py
from datasets import load_dataset
import openai
import os
import csv
import time
from dotenv import load_dotenv

def construct_query_from_schema(data):
    present_tense = 'do' if data['pronoun'] == 'they' else 'does'
    statement = data['text']
    pronoun = data['pronoun']
    options = data['options']
    query = f"S: {statement}\nQ: In the previous statement, {present_tense} '{pronoun}' refer to {options[0]} or {options[1]}?\nA: "
    return query

def convert_first_letter_to_lowercase(answer):
    return answer[0].lower() + answer[1:]

def create_csv(data, filename):
    with open(filename, 'w', newline='') as file:
        writer = csv.writer(file)
        writer.writerows(data)

def get_openai_answer(query_prompt):
    init_prompt_starter = "The following are pairs of Winograd Schema in the form of a statement S, a question Q and an answer A:"
    init_prompt1 = "S: The cat went through the door, but it's tail got stuck.\nQ: In the previous statement, does 'it' refer to The cat or The door?\nA: The cat"
    init_prompt2 = "S: The cat tried to go through the door, but it was too small.\nQ: In the previous statement, does 'it' refer to The cat or The door?\nA: The door."
    init_prompt3 = "S: Fedex made more profit than UPS last year, but that was mostly due to the success of the new delivery system they implemented.\nQ: In the previous statement, do 'they' refer to Fedex or UPS?\nA: Fedex."
    init_prompt4 = "S: Sam tried to buy Xerxes lunch, but he wouldn't allow it.\nQ: In the previous statement, does 'he' refer to Sam or Xerxes?\nA: Xerxes."  
    prompt = f'{init_prompt_starter}\n{init_prompt1}\n{init_prompt2}\n{init_prompt3}\n{init_prompt4}'

    response = openai.ChatCompletion.create(
        # model='gpt-3.5-turbo',
        model='gpt-4',
        messages=[
            {"role": "system", "content": prompt},
            {"role": "user", "content": query_prompt},
        ],
        temperature=0,
        max_tokens=200,
        top_p=1,
        frequency_penalty=0.0,
        presence_penalty=0.0
    )
    return response.choices[0]["message"]["content"].strip().replace('.', '')


print('----- start winograd schema challenge -----')
load_dotenv()
filename = 'winograd_schema_challenge.csv'
openai.api_key = os.getenv('OPENAI_API_KEY')
wsc273_dataset = load_dataset('winograd_wsc', name='wsc273',  split='test')

data_csv = [
    ['Statement', 'Question', 'Answer', 'AI Answer', 'Correct']
]
number_of_correct = 0
number_of_questions = len(wsc273_dataset)
for index, data in enumerate(wsc273_dataset):
    print(f'Question {index + 1}')
    correct = False
    query = construct_query_from_schema(data)
    query_split = query.split('\n')
    statement = query_split[0][3:]
    question = query_split[1][3:]
    answer = data['options'][data['label']]
    query_prompt = construct_query_from_schema(data)
    openai_answer = get_openai_answer(query_prompt)
    print(f'ai answer: {openai_answer}, answer: {answer}')

    if convert_first_letter_to_lowercase(openai_answer) == convert_first_letter_to_lowercase(answer):
        print('----- \U00002705 CORRECT! \U0001F44D -----')
        number_of_correct += 1
        correct = True
    else:
        print('----- \U0000274C INCORRECT! \U0001F44E -----')
    
    data_csv.append(
        [
            statement, question, answer, openai_answer, correct
        ]
    )
    time.sleep(3)

create_csv(data_csv, filename)
print(f'Accuracy rate = {int(number_of_correct/number_of_questions * 1000)/10}%({number_of_correct}/{number_of_questions})')



実行結果

  • コンソール


  • winograd_schema_challenge.csv
Statement,Question,Answer,AI Answer,Correct
The city councilmen refused the demonstrators a permit because they feared violence.,"In the previous statement, do 'they' refer to The city councilmen or The demonstrators?",The city councilmen,The city councilmen,True

...
Carol believed that Rebecca regretted that she had stolen the watch.,"In the previous statement, does 'she' refer to Carol or Rebecca?",Rebecca,Rebecca,True

はじめに

最近『教養としてのAI講義』という本を読みました。

著者のメラニー・ミッチェルは昔『複雑系の世界』という本を読んで(途中で挫折して、一部を破いて保存して)いましたので、名前は知っていました。その本には最初自分の経歴を話していて、「ダグラス・ホフスタッターの『ゲーデルエッシャー・バッハ』(GEB)に決定的な影響を受けて研究者になった」ということが書かれていました。『教養としてのAI講義』でもホフスタッターの話から始まります。

この本は題名通りAIについて広く議論されています。AIの歴史やAIによる倫理的な問題だけでなく、AI仕組み(ディープラーニング強化学習)も議論しているところもあります。

この本はとてもいい本ですが、原著は2019年出版であり少し古く、GPT(Generative Pretrained Transformer)のことは書かれていません。

この本の第4部には自然言語処理についてのことが書かれていました。

チューリングテストとその欠点

「機械は思考できるのか?」という問いにたいして、チューリングは現在「チューリングテスト」と呼ばれるものを提案しました。これは次のようなイミテーションゲーム(模倣ゲーム)です。コンピュータと人間の「2名」が参加して、もう1人の人間の審査員が、どちらが人間でどちらがコンピュータかを当てるために、それぞれに別々に質問します。審査員は2名の参加者から離れたところでキーボードの文章でやりとりします。

もしこのゲームをうまくこなせるコンピュータができたら、つまり審査員がどちらが人間でどちらがコンピュータかが区別できなければ、それは「コンピュータは真に思考している」と考えてもいいのではないか?ということです。


しかしチューリングテストには問題があります。比較的簡単なプログラムで人を簡単に騙すことができるからです。さらにもしもコミュニケーションしている言語がおかしかったとしても「僕はウクライナティーネイジャー。英語はかなり上手だけど完璧じゃないよ」と設定すれば、審査員は言葉の不自然さを疑問に思わなくなります(実際に2014年にチューリングテストに合格したプログラムはそのような設定がされていました)。

機械は言葉の意味がわからなくても人と自然に会話ができます。そこで機械が文章の意味を「理解」しているかどうかを判定するテストが考案されました。それがウィノグラード・スキーマ(winograd schema)です。

ウィノグラードスキーマ

ウィノグラードスキーマとはある簡単な文章に対して、「それ」が何を表しているかを問う問題です。ただし一つの単語が変わるだけで「それ」を表すものが全く変わるような問題となっているところがポイントです。次の文章はウィノグラードスキーマの例です。

  • The city councilmen refused the demonstrators a permit because they feared violence. (市議会員はデモ隊に許可を与えなかった。なぜなら、彼らは暴動を恐れたから。)

  • 「彼ら」は、”councilmen”を表していますか? それとも”demonstrators”を表していますか?

  • councilmen


  • The city councilmen refused the demonstrators a permit because they advocated violence. (市議会議員はデモ隊に許可を与えなかった。なぜなら、彼らは暴動を支持したから。)
  • 「彼ら」は、”councilmen”を表していますか? それとも”demonstrators”を表していますか?

  • demonstrators


チューリングテストはコンピュータが文章を全く理解していなくても正解を出せることができますが、ウィノグラードスキーマを考案した研究者によると、ウィノグラードスキーマテストではそのような可能性を未然に防いでいるとのことです。

この本によると、出版当時つまり2019年時点でウィノグラードスキーマ問題に対して最もよい正解率は61パーセントだったとのことです。

いくつかの自然言語処理研究グループは、
ウィノグラードスキーマの問題を解くためにさまざまな方法で実験を行っている。
これを書いている時点で最も高い成績を収めたプログラムの場合、
250題のウィノグラードスキーマ問題で約61パーセントの正解率だ。

ch.13 何でも聞いて section 「それ」は何を意味するのか?


これを読んだとき、「それではChatGPT4ではウィノグラードスキーマ問題の正解率はどうなのだろう?」と思いました。そこでプログラムを作成しました。

プログラム

プログラムの詳細は今回は省略します。上記のように書けば問題なく動くと思います。ただしChatGPT4を利用するためにはAPIキーを取得して、GPT4が使えるように申請しなければなりません。そして実際に利用するときはいくらかお金を支払わなければなりません。そこが少し障害となります。

結果

プログラムを作成して実際に試してみたら、91.5%(正解数 250問/総数 273問)でした。GPT3.5でも試して比較しようと思いましたが、エラーが生じてしまいうまくいきませんでした(このエラーは通信が集中しているから無理というものでした)。ただこの記事によると正解率はGPT-3では68.8%であり、GPT-4では94.4%だったそうです。

おわりに

今回は簡単に実装したかったので、すでに存在している英語のウィノグラードスキーマのデータセットを利用しました。日本語のウィノグラードスキーマ問題もウェブ上にはありましたが、データセットとしては見つかりませんでした。日本語バージョンのテストを実装するためにはデータセットの作成からおこなわなければなりませんでしたので、日本語での正解率は気になりますが、今回はスキップしました。もし時間があれば日本語のバージョンも試したいと思いました。


正解率が61%から数年で90%以上に向上したのは凄いなと思いました。

実はこの本にはロング・ベッツという賭けを紹介しています。その一番最初の賭けが「2029年までにチューリングテストを合格するコンピュータは存在しない」というものでした(a long bet bet 1)。予測者はモジラ・ファンデーションの初代理事長であるミッチェル・ケイパー(Mitchell Kapor)で、反対者はシンギュラリティの提唱で有名なレイ・カーツワイル(Ray Kurzweil)です。ここでのチューリングテストは不正や騙しがおこなわないためにかなり厳しい条件が課せられています。その条件下でケイパーは「2029年までには無理だ」と主張して、対してカーツワイルは「できる」と主張しています。著者のミッチェルはケイパー側です。

第3章で取り上げた、「綿密に練られたチューリングテストに合格するプログラムは実現しない」という
レイ・カーツワイルとミッチェル・ケイパーとの「長期間の賭け」は、2029年に結果が出る。
私はケイパーに賭ける。なぜなら「はじめに」で紹介した、
「人間の知性はすばらしいが、謎に包まれた奥深いものであり、
その大半が解明されていない。
そのためまったく同じものがつくられる恐れは当分ない」というケイパーの言葉に、
全面的に同意しているからだ。

ch.16 質問、答え、それについての考察
section 質問ー汎用的な人間レベルのAIの現実まで、あとどれくらいかかるのだろうか?


しかしこの賭けは2029年を待たず、現在(2023年)チューリングテストをおこなってもカーツワイルが勝つのではないかと思ってしまいます。勝手な想像ですが、綿密に練られたチューリングテストを合格する機械の存在は、2029年でようやくできるかできないかのギリギリの期間なのではないかと、賭けがおこなわれた当時(2002年)では想定されていたのではないかと思います。それがもはや6年も前にテストしてもほぼ確実に合格できるレベルなのですから、AIの進歩は想像以上なのだろうと思いました。

参考文献




僕から以上

人工知能・ベーシックインカム・デジタル直接民主主義

概要

来るべき未来における新たな社会について私の考えの概要を述べる。まだアイディアに過ぎず、これから研究しなければならないが、アイディアは以下の通りである。


近い将来人工知能(Artificial Intelligence: AI)がより発展することによって、我々は多くの仕事を失うことになる。これは全員が失業者となる未来である。全員が失業するので、全員に失業手当を充てる。つまり全員に一定の金額を給付するのである。これは基本所得制(Universal Basic Income: UBIまたは単にBI)と呼ばれる制度である。これは貧困を解決する方法である。

全員に一定金額を支給する代わりに、これまでの社会保証制度を抜本的に変える。障害者手当などの一部の制度を除きほとんどの社会保障制度を廃止する。年金制度や医療費控除や非営利法人に支払われる公金などである。そしてそれに関連する官僚機構の解体である。

税の目的はBIの財源と広い意味での国防—警察や軍隊やサイバーなど—のためのみである。

近い将来、我々はあくせく働く必要がなくなる。というよりもそのように働くことができなくなる。これは脱労働社会の到来である。だが労働時間がなくなることにより、今後人々はいかに余暇を過ごすのかということが問題となるのではないか。これがケインズが1930年に『孫のための経済的可能性』で示唆した懸念である。「2030年には週15時間しか働かなくなり、これからは余暇の使い方が問題となる」1そう考えていた。

これまで労働として費やしてきた時間の代わりに、我々は政治をおこなうのである。つまり職業的政治家はこの世から消え去り、全員が政治家となるのである。それを可能にするのがデジタル直接民主主義(Digital Direct Democracy: DDD)または電子的直接民主主義(Electronic Direct Democracy: EDD)である。アリストテレスの言うことが正しければ、人間は政治的な動物である。だが古代のギリシャ(アテネ)では少数の所謂自由人のみが政治をおこない、それ以外の多くの奴隷が社会を支えていた。これが民主主義の原型であるが、同時に理想でもある。そしてまさにこの理想がテクノロジーによって実現可能となるのである。つまり、全ての人間がインターネットを通じて等しく政治に参加できる社会となるのである。これはマルクスが夢見た共産主義の姿でもある。2

これに対して共産主義社会では、各人はそれだけに固定されたどんな活動範囲ももたず、
それぞれの任意の[どこでもすきな]部門で、自分を発達させることができるのであって、
社会が生産全般を規制しているのである。
だからこそ、私はしたいと思うままに、
今日はこれ、明日はあれをし、朝に狩猟を、
昼に魚取りを、夕べに家畜の世話をし、
夕食後に批判をすることが可能になり、
しかも、けっして狩人、漁師、牧人、
あるいは批判家にならなくてよいのである。
ドイツ・イデオロギー(44)

詳細

AIについて

AIとは何か

  • AIはルールベースのものと機械学習のものがある。
  • 例えばルールベースは探索木であり、機械学習ディープラーニング(深層学習)である。
  • 近年特に顕著なのはディープラーニングである。
  • これは教師あり学習と呼ばれるものであり、答えが設定されている学習データを使用する。
  • 学習するためには大量のデータ(解答付き)が必要であり、そのデータを作るためには人間がおこなわなければならない。
  • データを学習するためには大量の計算が必要となるので(たくさんの)GPUが必要となる。


数年前にディープラーニングを勉強してそれ以降全く忘れたから、このぐらいしか知らない。これから勉強し直す。まずはディープラーニングの生みの親の一人であるルカン(Yann Le Cun)の本を読む3

AIには何ができるか。何ができないか。


全然わかりません。まずはドレイファスの『コンピュータには何ができないか―哲学的人工知能批判』4を読む。

AIの社会的影響について

  • 技術には2種類ある。ひとつは労働補完技術であり、もうひとつは労働置換技術である。
  • 歴史的には労働者や政治家は労働置換技術には否定的であり反発される。理由はそれによって雇用が破壊されるからである。たいして労働補完技術は労働者に支持される傾向がある。
  • AIは労働置換技術である。
  • さらにAIで失われる仕事は事務労働であり、それは多くの中間層をなす仕事である。
  • 失業した労働者は知的労働か肉体労働に移らなければならない。だがITのような知的労働は雇用をあまり生まず、多くの労働者は以前よりも賃金の低い肉体労働に従事せざるを得なくなる。
  • 所得格差が広がり、富裕層と貧困層の二極化がますます生じる。
  • したがってAIは政治家や労働者から反発される可能性が高い。
  • だがAIの発展を止めるとイノベーションが起こらない可能性がある(イギリスが一番最初に産業革命をおこなえたのは蒸気機関という労働置換技術にたいして政治家が阻害しなかったから)。


近未来のシナリオを予測したカーツワイルの『シンギュラリティは近い』を読む5。技術の社会的影響については『テクノロジーの世界経済史』を読んでいる6

BIについて

貧困と格差について

  • 国家の目的は不幸を最小限にすることである。ここでの不幸とは殺人や強盗などの個人的な犯罪行為から、飢餓や戦争や貧困や格差などの社会的なものまでである。
  • 資本主義の問題として格差が言われているが、それは正しくない。格差は問題ではない。問題は貧困である。そして貧困の有力な解決方法がBIである。

目的

  • 格差の二極化を軽減するため。
  • 貧困の解決。または減少させるため。
  • 生活保護のような資力調査(ミーンズテスト)をおこなう貧困対策には問題点があるため、それを解決するため。
  • 資力調査をおこなうことによる行政コストの削減のため。

制度設計

  • BIには左派的なものと右派的なものがある。
    • 左派的なものは追加型であり、既存の社会保障制度をほとんど維持したままBIを追加するものである。
    • 右派的なものは削除型であり、BIの導入の代わりに既存の社会保障制度をほとんど廃止するものである。
  • 私は理想的には右派的(リバタリアン)であるが、現実的にはいきなり廃止はできないので、少しずつ改革するほかないと考える。
  • 受給者の条件について。最も排他的な場合だとその国の国籍を持つ人である。この場合だと移民のインセンティブがマイナスへと働くだろう。というのもその国の国籍を取得しない限り—そしてそのためには現地の言語を覚えるなどの同化をしなければならない—移民の人は現地の人と金銭的な差が生じるからである。
  • だが通常は上記のような最狭義の条件ではなく、一定の条件を満たせば在留外国人にも受給対象となるだろう。
  • 人間は未来のことについて考えるのが苦手なので、毎月全額を支給するのではなく、貯蓄率からの差分を支給するようにする。
  • 例えば月7万円支給されるBIの場合、貯蓄率を10%に設定すると実際に給付されるのは6万3千円であり、残りの7千円は貯蓄(プール)される。これはもしものときの預金となる。
  • 貯蓄率は上げることも下げることも簡単できるようにする。ただし人間心理を利用していろいろナッジする(Wikipediaの寄付のように)。
    • アンカリングする。Wikipediaでは平均1,500円寄付されていると数字が表示されている。寄付者はそこから引っ張られる。同じように「平均貯蓄率は15%です」と記載する。
    • みんなやっています。Wikipediaでは平均1,500円寄付されていると言っている。みんなと同じに合わせる傾向があるのでそのバイアスを利用する。
    • 選択肢(ラジオボタン)と自由記入を追加する。人間はめんどくさがりなのでラジオボタンを選択しがちであるので、それを利用する。例えばラジオボタンには「10%・15%・20%・25%」として、それ以外を選択したい場合は自由記入欄にする。そうすれば特に「0%」などの極端を選択しづらくなる。

  • 給付金はそのときの経済状況やインフレ率によって変動するようにする。中央銀行のようにそれを決定する独立機関を設置する。ただし人間は損失回避の心理があるので、給付金が下がることに抵抗感を生むかもしれない。そのため設計は固定額と変動額のものにするべきかもしれない。

効果について

  • 実際にBIには貧困対策として有効なのか?
    • これまで世界の様々な地域で実験がおこなわれてきたが、よくわからない。肯定的な論文もあれば否定的な論文もある。ちゃんと調査しないといけない。
    • ただざっと見た感じでは「発展途上国では効果的だが先進国ではあまり効果がない、むしろ逆効果かもしれない」。または制度設計の問題かもしれず、給付金を調整したら成功するのかもしれない(失敗するのかもしれない)。
  • 様々な実験ではミクロの結果(個人)が得られているが、未だ国民全員への給付実験がおこなわれていないのでマクロの結果(インフレ・GDPなど)はわからない。シミュレーションなどの研究はおこなわれているが、最早一国全員への給付実験をおこなうべきではないか?

他の貧困対策について

  • 資力調査(ミーンズ・テスト)を伴う貧困対策には問題点がある。
    • 貧困の罠。貧しいため所得を増やす機会に恵まれない場合に発生する悪循環のこと。
    • 不安定性の罠。受給資格者に戻った場合にすんなり給付が再開されないと思えば、福祉受給者は短期の仕事に就くことを尻込みする。
    • 資力調査のために尊厳を傷つけられる。受給資格があるかどうかをチェックされることは当人に時間的にも精神的にも負担がかかる。資料を作成したり、屈辱的な質問を受けたり。
    • 補足率の低下。補足率とは実際には受給されなければならない人が受給されない確率である。つまり必要な人に手当が行き渡っていない。
  • 負の所得税
  • 雇用保証制度
  • 慈善
  • データベース設計の観点から考えると、ベーシックインカム以外の社会制度では国家には「所得」のデータ(カラム)が必要となる。対してベーシックインカムでは「所得」データは不要である。「国家はできるだけ国民の個人情報を持つべきではない」という原則に従うと、ベーシックインカムの方がより安全であるだろう。


「問題は格差ではなく、貧困である」というのは決定的な主張である。これはピンカーが『21世紀の啓蒙(上)』の第9章で言っていた7。さらに論証しなければならない。そのためにはピケティ8のあの分厚い本や他の人の格差論も読まねばならない。まずは『貧困』から読む9。さて絶対的貧困相対的貧困の違いはなんだ? ジニ係数ってなんだ?

ベーシックインカムの教科書であるスタンディングの本は必読である10。ただこの本ではリバタリアン的なものを批判していた(Ch.3, Sec. リバタリアンの視点)。リバタリアンベーシックインカム論を知りたいので、今後はZwolinskiの新刊(未刊行)を読む11

ベーシックインカムに貯蓄率を追加するというのは今のところどこにも記載されていない。この辺は『ナッジ』にあるリバタリアンパターナリズムからヒントを得た12。ただスタンディングはリバタリアンパターナリズムに批判的である(Ch.2, Sec. 家族関係と正義, Ch.3, Sec. リバタリアンパターナリズムの危険性)。

財源については余計な支出(行政コスト)を削減した上での、増税である。何を財源とするかは難しいところだが、公平性の観点から言えば所得税となるだろう。だが、リバタリアン的にはそれで問題ないのか、まだわからない。

また増税なしに国債発行で問題ないとする考えもある。その一つがMMTである。MMTによると「過度なインフレにならない限り、自国通貨を発行している政府はいくらでも借金してかまわない」からである。もっともMMTは政策的には就業保証プログラム(Job Guarantee Program)を支持しているので、多くのMMT論者はベーシックインカムには否定的である。だが、一部にはMMT論者でベーシックインカム支持者もいる13

スタンディングはベーシックインカムの導入の理由において「社会正義」を掲げている。だが私はベーシックインカムにそこまでの強い主張を持っていない。もしも効率的な貧困対策ができるならばそれでいいと考えている。つまり、できるだけ行政コストが少ない中でより大きい貧困解決のパフォーマンスがあればそれでよい。逆に言えばもしベーシックインカムが行政コストを肥大化させ、かつそれほど貧困対策となっていなければ私の主張は潔く捨て去る。それは私が経験主義であるからである。つまり「知識の究極的な根拠は理性ではなく経験である」ということである。確かに理論的にはベーシックインカムは他の貧困対策に比べて、コストパフォーマンスが高いと考えられるが、実際やってみなければわからない。そしてしばしば理論は外れる。

税金の目的

  • 太古の昔から人々が豊かになる方法は収奪することしかなかった。それは文字通りの強盗というのもあれば、賄賂というのもあった。それを国家が正当化したものが税金である。税金という収奪の唯一の正当化は国防である。
  • 資本主義の肯定: 富はゼロサムゲームであった。だが資本主義がこの状況を一変させた。富を増やすことができるようになった。
  • 私有財産の肯定: 私有権の否定こそが共産主義であるが、それは人間の本性に合わない非合理な思想である。
    • いかなる公共権も認めない(つまりすべて私有権である)というのも、いかなる私有権も認めない(つまりすべて公共権である)というのも、両者とも排斥する。


税についてはまだちゃんと調べていない。アイディアはハッカーの本から得た^Graham。ただこれが正しいのかはわからない。

リバタリアンの思想を理解しなければならないので、ノージックの本を読む14。またフリードマンのも読む15

私有財産について考えたいために『人はなぜものを欲しがるのか』16という本を読んだが、あまり参考にならなかった。当初は私有財産の起源を進化論的に説明するのかと思って読んでみたが、実際はそういうことはなく(もしかしたらあったかも?)、児童心理学から説明していたので、期待と異なっていた。結局飽きてしまい途中で止めた。

DDDについて

民主主義について

  • 間接民主主義には問題がある。
  • 直接民主主義のデメリットはテクノロジーによって解決できる。
  • 職業的政治家が消え去る。世襲議員が消える。これ以上の喜ばしいことがあるだろうか!
  • 組織票もなくなる。
  • 有権者が政治家に「なぜこれをやらないのか!? 今の政治はおかしい!」と不平不満を言うことがなくなる。なぜなら我々全員が有権者でもあり政治家でもあるからである。自分達で政治ができるのである。こんなにいいことはないではないか— 実際文句を言っている連中はただ無責任なことを言いただけならば話は別だが。
  • 余計な税金がなくなる。政党助成金なんてものはなくなる!


まずは民主主義の歴史から勉強する。民主主義自体の本はたくさんあるが、直接民主主義やデジタル民主主義はあまり見当たらなかった。少なくとも日本語では。これからちゃんと勉強する。Roslyn Fullerという政治学者がいることを知った。この人は民主主義の研究をしていて、デジタルデモクラシーについても議論している。『Principles of Digital Democracy』17という本が今年出るので(まだ未刊行)、それを読む。

労働について

来るべき未来の社会は多かれ少なかれ労働しない社会となる。それは果たして良い社会なのだろうか?

グレーバーによると世界には「クソどうでもいい仕事」が存在している18。「ベーシックインカムの究極的な目的は、生活を労働から切り離すことにある」(Ch.7, Sec. 仕事と報酬を切り離し本書で論じてきたジレンマを集結させる構想の一案としての普遍的ベーシックインカム)。それによってクソどうでもいい仕事は消え去り、我々は真に有益な仕事に従事することができるようになるという。

アーレントによると人間の働きには「仕事」「労働」「活動」の3つあるという19古代ギリシャでは「労働」は奴隷のものであるために、自由人は労働を軽蔑して、活動をおこなっていた。ここでの活動とは「政治」である。だが、ルネッサンスから「労働」の価値が高くなり、マルクスに至って近代以降「労働」が最高の価値となった(アーレントの本はまだ読んでいないのでちゃんと読む)。

今こそ近代から続いた労働観を転換すべき時ではないだろうか?

おわりに

これから1年〜2年ぐらいかけてまとめる。本に出版したいとも思っている。日本語版ができたらすぐに英語版を出版したいと思っているから基本的には外国の文献しかない。日本語の著作は直接引用はしないけれども背景知識として隠れている。特に井上智洋さんの本に影響されている(20)(21)(22)。直接指導してもらおうかな。

できるように頑張る。ちょこちょこ書く。




僕から以上


  1. 孫たちの経済的可能性
  2. ドイツ・イデオロギー
  3. ディープラーニング 学習する機械 ヤン・ルカン、人工知能を語る
  4. コンピュータには何ができないか―哲学的人工知能批判
  5. シンギュラリティは近い―人類が生命を超越するとき
  6. テクノロジーの世界経済史 ビル・ゲイツのパラドックス
  7. 21世紀の啓蒙 上: 理性、科学、ヒューマニズム、進歩
  8. 21世紀の資本
  9. 14歳から考えたい 貧困 【ベリー・ショート・イントロダクション】
  10. スタンディング ベーシックインカムへの道 ―正義・自由・安全の社会インフラを実現させるには
  11. Matt Zwolinski Universal-Basic-Income-Everyone-Needs
  12. NUDGE 実践 行動経済学 完全版
  13. ベーシックインカム×MMT(現代貨幣理論)でお金を配ろう: 誰ひとり取り残さない経済のために
  14. アナーキー・国家・ユートピア―国家の正当性とその限界
  15. 資本主義と自由 (日経BPクラシックス)
  16. 人はなぜ物を欲しがるのか
  17. Principles of Digital Democracy Theory and Case Studies
  18. ブルシット・ジョブ クソどうでもいい仕事の理論
  19. 人間の条件
  20. 純粋機械化経済 頭脳資本主義と日本の没落 (日本経済新聞出版)
  21. AI時代の新・ベーシックインカム論 (光文社新書)
  22. 「現金給付」の経済学 反緊縮で日本はよみがえる (NHK出版新書)

Macで別バージョンのPythonの仮想環境を構築する方法

概要

Macにおいて仮想環境を作成して、そこにすでにインストールされているバージョンとは異なるPythonを構築する方法をまとめる。

: 自分用のためここに記載されたこと以外のことを設定しなければならない可能性がある。

前提条件

  • Macであること。
  • コマンドpyenvがインストールされていること。

やりたいこと

  • 現状自分のMacには3.9.23.10.7のバージョンのPythonがインストールされている。
  • やりたいことは特定のディレクトリに仮想環境を作成して、そこに新しいバージョンのPython(3.11.3)にしたい。

  • before:

  • after:

方法

STEP 1 pyenvをアップデートする

  • 最新のPythonのバージョンをインストールしたいので、pyenvをアップデートします。pyenvには直接アップデートのコマンドがありませんので、次のコマンドを実行します。
cd ~/.pyenv/plugins/python-build/../.. && git pull && cd -

STEP 2 インストールしたいバージョンを確認する

  • インストールしたいPythonのバージョンを次のコマンドで確認します。
Available versions:
  2.1.3
  2.2.3
  2.3.7
  2.4.0
  2.4.1

(略)

  3.8.16
  3.9.0
  3.9-dev
  3.9.1
  3.9.2
  3.9.4
  3.9.5
  3.9.6
  3.9.7
  3.9.8
  3.9.9
  3.9.10
  3.9.11
  3.9.12
  3.9.13
  3.9.14
  3.9.15
  3.9.16
  3.10.0
  3.10-dev
  3.10.1
  3.10.2
  3.10.3
  3.10.4
  3.10.5
  3.10.6
  3.10.7
  3.10.8
  3.10.9
  3.10.10
  3.10.11
  3.11.0
  3.11-dev
  3.11.1
  3.11.2
  3.11.3
  3.12.0a7
  3.12-dev
  activepython-2.7.14
  activepython-3.5.4
  
  (略)

  stackless-3.4.7
  stackless-3.5.4
  stackless-3.7.5

STEP 3 インストールしたいバージョンをインストールする

  • 確認後に次のコマンドでインストールします。インストールするまで少し時間がかかります(1分ぐらい)。
pyenv install <version>

今回はpyenv install 3.11.3です。

STEP 4 インストールしたことを確認する

  • 実際うまく期待通りのバージョンがインストールされたかどうかを次のコマンドで確認します。問題なく3.11.3が追加されています。
pyenv versions

* system (set by /Users/yoheiwatanabe/.pyenv/version)
  3.9.2
  3.10.7
  3.11.3

  • ここでは設定はsystemが設定されています(*が目印)。

STEP 5 仮想環境に設定したいバージョンに変更する

  • 仮想環境に設定したいバージョンに次のように変更します。
pyenv local 3.11.3
  • pyenv versionsで確認すると*3.11.3に移動しています。

STEP 6 仮想環境を設定する

  • 設定したいディレクトリ配下に移動して、仮想環境を次のように設定します。今回は仮想環境名を.venvとしていますが、それは任意で問題ありません。
python -m venv .venv

STEP 7 仮想環境に入る/出る

  • 仮想環境にはsource .venv/bin/activateで入ります(アクティベイトします)。
  • バージョンは期待通り3.11.3となっています。
  • 仮想環境から出る場合(デアクティベイト)はdeactivateを実行します。







僕から以上

Django REST Frameworkのモデルシリアライザーのフィールド名をハイフンに変更する方法

概要

Django REST Frameworkのモデルシリアライザー(serializers.ModelSerializer)のフィールド名にあるアンダースコア(_)をハイフン(-)に変更する方法をまとめる。



要点

モデルシリアライザーにハイフンを利用したい場合、シリアライザーのクラスMetaを次のように修正してください。

    class Meta:
        """Meta"""
        model = MODEL
        fields = ['hyphen-field']
        extra_kwargs = {
            'hyphen-field': {
                'source': 'model_field'
            }
        }



したいこと

  • 書店のデータベースを作成します(Store)
  • 登録されている書店を取得するAPIを作成します(GET: http://127.0.0.1:8000/api/stores/)
  • クエリがついていない場合は、すべての書店情報が取得されます。
  • クエリ(store-name)がついている場合は、クエリのバリデーションをおこない問題なければ、それを含む書店名の書店情報が取得されます。

背景

どうやらAPIのクエリはアンダースコアよりハイフンのほうが良いとのことです。しかしDjangoのモデルのフィールドはアンダースコアで記載されています。したがってクエリのバリデーションをおこなうモデルシリアライザーを作成したい場合、アンダースコアからハイフンに変更したいと思いました。

ソースコード

モデル

from django.db import models


class Store(models.Model):
    """Store model"""
    store_name = models.CharField(max_length=100)
    address = models.CharField(max_length=300)


リアライザー

from rest_framework import serializers
from backend.books.models.store import Store

class StoreSerializer(serializers.ModelSerializer):
    """Store serializer"""

    class Meta:
        """Meta"""
        model = Store
        fields = '__all__'


class StoreQuerySerializer(serializers.ModelSerializer):
    """Store query serializer"""

    class Meta:
        """Meta"""
        model = Store
        fields = ['store-name']
        extra_kwargs = {
            'store-name': {
                'source': 'store_name'
            }
        }


ビュー

from rest_framework import generics
from rest_framework.response import Response
from backend.books.serializer import StoreQuerySerializer, StoreSerializer


class StoreList(generics.ListAPIView):
    """Store list"""
    permission_classes = []
    serializer_class = StoreSerializer
    queryset = Store.objects.all()

    def list(self, request, *args, **kwargs):
        if not request.query_params:
            return super().list(request, *args, **kwargs)
        query_serializer = StoreQuerySerializer(data=request.query_params)
        query_serializer.is_valid(raise_exception=True)
        store_name = query_serializer.validated_data['store_name']
        queryset = Store.objects.filter(store_name__contains=store_name)
        return Response(data=self.get_serializer(queryset, many=True).data)


ソースコードについていくつか指摘をしたいと思います。

  • 最初のif文はクエリが設定されいるかどうかを確認しています。何もなければ通常のlist関数が実行されて、すべてのデータを取得します。
  • そうでない場合、つまりクエリが設定されている場合、StoreQuerySerializerでクエリのバリデーションをおこないます。query_serializer.is_valid(raise_exception=True)により、不適切なクエリの場合、エラーが返ります。
  • 適切なクエリが設定されている場合、バリデーションを通過します。そのときquery_serializer.validated_data['store_name']store-nameのクエリがあります。
  • あとはクエリに設定されたもの(store-name)を含むデータを絞り込んで返します。


URL

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from backend.books.views import StoreList

router = DefaultRouter()

urlpatterns = [
    path('stores/', StoreList.as_view()),
    path('', include(router.urls)),
]



実行

データベース

id store_name address
1 岩波書店 東京都千代田区一ツ橋2丁目5番5号
2 講談社 東京都文京区音羽 2-12-21
3 幻冬舎 東京都渋谷区千駄ケ谷四丁目9番7号
4 中央公論新社 東京都千代田区大手町1-7-1 読売新聞ビル19階
5 筑摩書房 東京都台東区蔵前2-5-3
6 みすず書房 東京都文京区本郷2-20-7



結果

クエリなし

期待値はすべてのStoreデータが取得されることです。

  • メソッド: GET
  • リソース: http://127.0.0.1:8000/api/stores/

[
    {
        "id": 1,
        "store_name": "岩波書店",
        "address": "東京都千代田区一ツ橋2丁目5番5号"
    },
    {
        "id": 2,
        "store_name": "講談社",
        "address": "東京都文京区音羽 2-12-21"
    },
    {
        "id": 3,
        "store_name": "幻冬舎",
        "address": "東京都渋谷区千駄ケ谷四丁目9番7号"
    },
    {
        "id": 4,
        "store_name": "中央公論新社",
        "address": "東京都千代田区大手町1-7-1 読売新聞ビル19階"
    },
    {
        "id": 5,
        "store_name": "筑摩書房",
        "address": "東京都台東区蔵前2-5-3"
    },
    {
        "id": 6,
        "store_name": "みすず書房",
        "address": "東京都文京区本郷2-20-7"
    }
]


クエリあり

成功ケース

  • メソッド: GET
  • リソース: http://127.0.0.1:8000/api/stores/
  • クエリ: store-name=書房

[
    {
        "id": 5,
        "store_name": "筑摩書房",
        "address": "東京都台東区蔵前2-5-3"
    },
    {
        "id": 6,
        "store_name": "みすず書房",
        "address": "東京都文京区本郷2-20-7"
    }
]


失敗ケース

クエリにアンダースコアがついている場合は、エラーとなります。

  • メソッド: GET
  • リソース: http://127.0.0.1:8000/api/stores/
  • クエリ: store_name=岩波書店

{
    "store-name": [
        "この項目は必須です。"
    ]
}

GitHub - YoheiWatanabe/recording-books at test-serializer-convert-underscore-into-hyphen







僕から以上

Djangoのモデルでコメントの返信数を取得する方法

概要

SNSやコメントの返信において、返信数を取得する方法を提示する。


ソースコード

  • モデル
"""Comment Model"""

from django.db import models

class Comment(models.Model):
    """Comment model"""
    text = models.CharField(max_length=2000)
    reply_to = models.ForeignKey(to='self', on_delete=models.DO_NOTHING, null=True, blank=True, default=None)

    def __str__(self) -> str:
        return self.text



class CommentSerializer(serializers.ModelSerializer):
    """Comment serializer"""

    class Meta:
        """Meta"""
        model = Comment
        fields = '__all__'
    
    def to_representation(self, instance):
        """Representation"""
        response = super().to_representation(instance)
        response['reply_count'] = instance.number_of_replies if instance.number_of_replies else 0
        return response



  • ビュー
class CommentList(generics.ListAPIView):
    """Comment view"""
    permission_classes = []
    serializer_class = CommentSerializer
    queryset = Comment.objects.all()

    def get_queryset(self):
        """Get query set"""
        subquery = Comment.objects.filter(reply_to=OuterRef('id')).values('reply_to').annotate(count=Count('reply_to')).values('count')
        return Comment.objects.annotate(number_of_replies=Subquery(subquery))



  • URL
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from backend.books.views import CommentList

router = DefaultRouter()

urlpatterns = [
    path('comments/', CommentList.as_view()),
    path('', include(router.urls)),
]





データ

結果

[
    {
        "id": 1,
        "text": "This is a test.",
        "reply_to": null,
        "reply_count": 1
    },
    {
        "id": 2,
        "text": "This is a reply test.",
        "reply_to": 1,
        "reply_count": 0
    },
    {
        "id": 3,
        "text": "That is a test.",
        "reply_to": null,
        "reply_count": 0
    },
    {
        "id": 4,
        "text": "Who are you?",
        "reply_to": null,
        "reply_count": 3
    },
    {
        "id": 5,
        "text": "I'm a superman.",
        "reply_to": 4,
        "reply_count": 0
    },
    {
        "id": 6,
        "text": "I'm a mathematician.",
        "reply_to": 4,
        "reply_count": 0
    },
    {
        "id": 7,
        "text": "I'm an entertainer.",
        "reply_to": 4,
        "reply_count": 0
    }
]

GitHub - YoheiWatanabe/recording-books at test-comment-reply

解説

SNSの投稿やコメントには返信機能があります。それを実現するモデルの作成および返信数を取得する方法を解説します。

モデル

返信機能のあるコメントモデルCommentを作成します。簡単のために他のフィールドは除いています。

  • backend/backend/books/models/comment.py
from django.db import models

class Comment(models.Model):
    """Comment model"""
    text = models.CharField(max_length=2000)
    reply_to = models.ForeignKey(to='self',
                      on_delete=models.DO_NOTHING,
                      null=True,
                      blank=True,
                      default=None)

フィールドreply_toは返信(リプライ)を実装するためのものです。自分自身のidと紐付けます。そのためにForeign Keyにto='self'を設定します。

このフィールドはコメントが返信なのかどうかを表しています。普通のコメントの場合はnullとなり、返信のコメントの場合は返信しているIDが設定されます。

on_deleteはここでは返信されている元のコメントがなくなった場合どうなるかということです。on_deleteはとりあえずDO_NOTHINGにしています。元のコメントがなくなってもそのままにしています。CASCADEは元のコメントが削除されると、その返信も削除されるので問題があります。またPROTECTは返信がない限り、元のコメントが削除できなくなるので、それも不都合です。SET_DEFAULTnullとなってしまうので、返信が通常のコメントになってしまいこれもおかしくなります。結局DO_NOTHINGにしています。

ビュー

今回の肝はビュー(view)です。返信数を取得するためには愚直にやる方法もありますが、それだと大量のSQLを実行することになります(N + 1問題)。ですので、サブクエリを使って一発で実行します。

  • backend/backend/books/views.py
from django.db.models import OuterRef, Subquery, Count
from rest_framework import generics
from backend.books.serializer import CommentSerializer
from backend.books.models.comment import Comment

class CommentList(generics.ListAPIView):
    """Comment view"""
    permission_classes = []
    serializer_class = CommentSerializer
    queryset = Comment.objects.all()

    def get_queryset(self):
        """Get query set"""
        subquery = Comment.objects
        .filter(reply_to=OuterRef('id'))
        .values('reply_to')
        .annotate(count=Count('reply_to'))
        .values('count')
        
         return Comment.objects
          .annotate(number_of_replies=Subquery(subquery))

今回はgenerics.ListAPIViewを使っていますが、他のViewでも問題ありません。

解説をしたいのですが、まだ「これでうまくいった」ということしか言えず、ちゃんと理解していません...。

annotateがあるので、このクエリセットはnumber_of_repliesというフィールドがあります。

リアライザー

  • backend/backend/books/serializer.py
from rest_framework import serializers
from backend.books.models.comment import Comment

class CommentSerializer(serializers.ModelSerializer):
    """Comment serializer"""

    class Meta:
        """Meta"""
        model = Comment
        fields = '__all__'
    
    def to_representation(self, instance):
        """Representation"""
        response = super().to_representation(instance)
        response['reply_count'] = instance
                                   .number_of_replies
                                   if instance
                                   .number_of_replies
                                   else 0
        return response

to_representationを使って戻り値を修正しています。新たに返信数reply_countを追加しています。返信がない場合(単なるコメントの場合)、instance.number_of_repliesNoneとなります。このときは返信数を0にするために、三値演算子(if...else)を使っています。

URL

  • backend/backend/books/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from backend.books.views import CommentList

router = DefaultRouter()

urlpatterns = [
    path('comments/', CommentList.as_view()),
    path('', include(router.urls)),
]

コメントAPIを実行するためにURLを設定します。

アドミン

  • backend/backend/books/admin.py
from django.contrib import admin
from backend.books.models.comment import Comment

class CommentAdmin(admin.ModelAdmin):
    """Display Comment model"""
    model = Comment
    list_display = ('id', 'text', 'reply_to')

admin.site.register(Comment, CommentAdmin)

管理者画面でコメントのデータベース操作するための設定です。モデルのreply_toblank=Trueとなっているのは管理者画面から普通のコメントを作成できるようにするためです。もしもblank=Trueとなっていない場合、reply_toが空のままだとエラーが生じます。





僕から以上

Django モデルのフィールドのnullとblankについて

概要

Djangoモデルのフィールドにはnullblankのオプションがある。nullはデータベースに関するオプションであり、blankはアプリケーションのバリデーションに関するオプションである。

オプションそれぞれの場合について、どのような動作をするのか確認する。また、Djano REST Framework(DRF)の場合、APIを実行するとどのようになるのかも確認する。

要約

  • 文字列のフィールド場合(CharField, TextField)、空文字可能にするためには原則blank=Trueのみでいい。
  • それ以外のフィールドの場合(e.g.BooleanField, DateTimeField)、空データを可能にするためにはblank=Truenull=Trueが必要となる。
  • フォームでのバリデーションは不要だが、デフォルトが決まっている場合、models.BooleanField(blank=True, default=False)とすればよい。
  • blank=Falseかつnull=Trueはほぼ使われない。
  • DRFにおいてblankまたはnullの少なくとも1つがTrueである場合、APIのリクエストボディはバリデーションされない。

前提知識

  • 必須: Pythonの使い方を知っている。
  • 必須: Djangoの使い方を知っている。
  • 任意: Django REST Frameworkを知っている。
  • 要約
  • 前提知識
  • nullとblankについて
    • 1. null=Falseかつblank=False(デフォルト)の場合
    • 2. null=Trueかつblank=Falseの場合
    • 3. null=Falseかつblank=Trueの場合
    • 4. null=Trueかつblank=Trueの場合
  • 文字型フィールドの問題点
  • 実行結果
    • 管理者画面
    • API
  • まとめ
  • 参考文献
続きを読む

書評: ダニエル・C・デネット『ダーウィンの危険な思想 生命の意味と進化』

一言

何かすごいことが書かれているかのように醸し出しているが、あまりにも冗長すぎて挫折した退屈でつまらない本

概要

  • まだない。

感想

最初の3章ぐらいまではまだおもしろかったが、結局諦めた。理由は端的につまらない。そして結局何が言いたいのかわからないということである。

「自然選択(進化)はアルゴリズムである」や「生物学はエンジニアリングである」等のスローガンばかりが目立つだけで、具体的に何か言っているかというとそうでもない。

「なるほどね。『進化はアルゴリズム』なんだ。じゃあ、遺伝的アルゴリズムとかどうなの?そういうのを哲学的に分析したり批判したりしているの?」と思っても、そんなことは一切ない。もっとも遺伝的アルゴリズムのことは記載されているが(ch.8, sec.5)、だからと言ってそれを発見したエピソードがあるだけで特に分析や応用なんかはない。なんだこれ。

読んだ感じだと、「進化はアルゴリズムである」の意味はせいぜい「自然選択にはスカイフック(超自然的な跳躍)など一切なく、クレーン(現実的な漸進的なプロセス)の連続だ」ということにすぎない。それをなんだかすごいことを言っているかのように偽装している。

よくこんなつまらない鈍器を書いたものだ。訳者たちもよく訳したなと驚きに尽きる。正直最後の訳者の解説を読んで、あとはつまみ食いすればいいと思う。

デネットドーキンスやピンカーなどの現代の有名な知識人の一人である。だから一応どんな人なのか知るためにデネットの著作を読む。だがデネットの本は全て分厚い。直観ポンプの本(『思考の技法』)は一つのテーマを議論しているというよりも、さまざまな思考ツールを紹介しているものだから、まだ読みやすい。けれども他の本はもう読まない。

(未完)