言語処理100本ノック2020 第10章 機械翻訳

はじめに

言語処理100本ノック2020

言語処理100本ノックは東北大学が公開している自然言語処理の問題集です。

とても良質なコンテンツで企業の研修や勉強会で使われています。

そんな言語処理100本ノックが2020年に改定されてました。昨今の状況を鑑みて、深層ニューラルネットワークに関する問題が追加されました。(その他にも細かい変更があります)

この記事では、言語処理100本ノック2020にPythonで取り組んでいきます。

他にも色々な解法があると思うので、一つの解答例としてご活用ください!

全100問の解説に戻る

90. データの準備

機械翻訳のデータセットをダウンロードせよ.訓練データ,開発データ,評価データを整形し,必要に応じてトークン化などの前処理を行うこと.ただし,この段階ではトークンの単位として形態素(日本語)および単語(英語)を採用せよ.

京都フリー翻訳タスク (KFTT)データセットからダウンロードし、展開する。

1
tar -zxvf kftt-data-1.0.tar.gz

ファイルは以下のような構成となっている。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
kftt-data-1.0/
kftt-data-1.0/data/
kftt-data-1.0/data/orig/
kftt-data-1.0/data/orig/kyoto-tune.en
kftt-data-1.0/data/orig/kyoto-dev.ja
kftt-data-1.0/data/orig/kyoto-dev.en
kftt-data-1.0/data/orig/kyoto-train.en
kftt-data-1.0/data/orig/kyoto-tune.ja
kftt-data-1.0/data/orig/kyoto-train.ja
kftt-data-1.0/data/orig/kyoto-test.ja
kftt-data-1.0/data/orig/kyoto-test.en
kftt-data-1.0/data/tok/
kftt-data-1.0/data/tok/kyoto-tune.en
kftt-data-1.0/data/tok/kyoto-dev.ja
kftt-data-1.0/data/tok/kyoto-train.cln.en
kftt-data-1.0/data/tok/kyoto-dev.en
kftt-data-1.0/data/tok/kyoto-train.en
kftt-data-1.0/data/tok/kyoto-tune.ja
kftt-data-1.0/data/tok/kyoto-train.cln.ja
kftt-data-1.0/data/tok/kyoto-train.ja
kftt-data-1.0/data/tok/kyoto-test.ja
kftt-data-1.0/data/tok/kyoto-test.en
kftt-data-1.0/README.txt
  • サブディレクトリtok/以下のファイルはトークン化されています。そのためこれをそのまま利用します
  • 重み学習(tune)は利用しません
  • train.clnは0単語の文や非常に長い(40単語以上)の文を除いたデータで、これを学習データとして利用します

つまり、以下のファイルを利用します。

1
2
3
4
5
6
kftt-data-1.0/data/tok/kyoto-train.cln.ja
kftt-data-1.0/data/tok/kyoto-train.cln.en
kftt-data-1.0/data/tok/kyoto-dev.ja
kftt-data-1.0/data/tok/kyoto-dev.en
kftt-data-1.0/data/tok/kyoto-test.ja
kftt-data-1.0/data/tok/kyoto-test.en

91. 機械翻訳モデルの訓練

90で準備したデータを用いて,ニューラル機械翻訳のモデルを学習せよ(ニューラルネットワークのモデルはTransformerやLSTMなど適当に選んでよい).
Attentionを導入したSeq2Seqモデルを学習させます。コードの大部分は、PyTorchの公式チュートリアルを利用しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
from __future__ import unicode_literals, print_function, division
from io import open
import unicodedata
import string
import re
import random

import torch
import torch.nn as nn
from torch import optim
import torch.nn.functional as F

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SOS_token = 0
EOS_token = 1
class Lang:
def __init__(self, name):
self.name = name
self.word2index = {}
self.word2count = {}
self.index2word = {0: "SOS", 1: "EOS"}
self.n_words = 2 # Count SOS and EOS

def addSentence(self, sentence):
for word in sentence.split(' '):
self.addWord(word)

def addWord(self, word):
if word not in self.word2index:
self.word2index[word] = self.n_words
self.word2count[word] = 1
self.index2word[self.n_words] = word
self.n_words += 1
else:
self.word2count[word] += 1
1
2
3
4
5
6
7
8
def normalizeString(s):
t = s
s = s.lower().strip()
s = re.sub(r"([.!?])", r" \1", s)
s = re.sub(r"[^a-zA-Z.!?]+", r" ", s)
if len(s.replace(' ','')): # ascii文字以外
return s
return t
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def readLangs(lang1, lang2):
print("Reading lines...")
with open(lang1) as f:
lines1 = f.readlines()
with open(lang2) as f:
lines2 = f.readlines()
pairs = []
for l1,l2 in zip(lines1,lines2):
l1 = normalizeString(l1.rstrip('\n'))
l2 = normalizeString(l2.rstrip('\n'))
pairs.append([l1,l2])
input_lang = Lang(lang1)
output_lang = Lang(lang2)
return input_lang, output_lang, pairs
1
2
3
4
5
MAX_LENGTH = 10
def filterPair(p):
return len(p[0].split(' ')) < MAX_LENGTH and len(p[1].split(' ')) < MAX_LENGTH
def filterPairs(pairs):
return [pair for pair in pairs if filterPair(pair)]
1
2
train_ja = 'kftt-data-1.0/data/tok/kyoto-train.cln.ja'
train_en = 'kftt-data-1.0/data/tok/kyoto-train.cln.en'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

def prepareData(lang1, lang2):
input_lang, output_lang, pairs = readLangs(lang1, lang2)
print("Read %s sentence pairs" % len(pairs))

pairs = filterPairs(pairs)

print("Trimmed to %s sentence pairs" % len(pairs))
print("Counting words...")

for pair in pairs:
input_lang.addSentence(pair[0])
output_lang.addSentence(pair[1])

print("Counted words:")
print(input_lang.name, input_lang.n_words)
print(output_lang.name, output_lang.n_words)
return input_lang, output_lang, pairs


input_lang, output_lang, pairs = prepareData(train_ja, train_en)
print(random.choice(pairs))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class EncoderRNN(nn.Module):
def __init__(self, input_size, hidden_size):
super(EncoderRNN, self).__init__()
self.hidden_size = hidden_size

self.embedding = nn.Embedding(input_size, hidden_size)
self.gru = nn.GRU(hidden_size, hidden_size)

def forward(self, input, hidden):
embedded = self.embedding(input).view(1, 1, -1)
output = embedded
output, hidden = self.gru(output, hidden)
return output, hidden

def initHidden(self):
return torch.zeros(1, 1, self.hidden_size, device=device)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class AttnDecoderRNN(nn.Module):
def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):
super(AttnDecoderRNN, self).__init__()
self.hidden_size = hidden_size
self.output_size = output_size
self.dropout_p = dropout_p
self.max_length = max_length

self.embedding = nn.Embedding(self.output_size, self.hidden_size)
self.attn = nn.Linear(self.hidden_size * 2, self.max_length)
self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)
self.dropout = nn.Dropout(self.dropout_p)
self.gru = nn.GRU(self.hidden_size, self.hidden_size)
self.out = nn.Linear(self.hidden_size, self.output_size)

def forward(self, input, hidden, encoder_outputs):
embedded = self.embedding(input).view(1, 1, -1)
embedded = self.dropout(embedded)

attn_weights = F.softmax(
self.attn(torch.cat((embedded[0], hidden[0]), 1)), dim=1)
attn_applied = torch.bmm(attn_weights.unsqueeze(0),
encoder_outputs.unsqueeze(0))

output = torch.cat((embedded[0], attn_applied[0]), 1)
output = self.attn_combine(output).unsqueeze(0)

output = F.relu(output)
output, hidden = self.gru(output, hidden)

output = F.log_softmax(self.out(output[0]), dim=1)
return output, hidden, attn_weights

def initHidden(self):
return torch.zeros(1, 1, self.hidden_size, device=device)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def indexesFromSentence(lang, sentence):
return [lang.word2index[word] for word in sentence.split(' ')]


def tensorFromSentence(lang, sentence):
indexes = indexesFromSentence(lang, sentence)
indexes.append(EOS_token)
return torch.tensor(indexes, dtype=torch.long, device=device).view(-1, 1)


def tensorsFromPair(pair):
input_tensor = tensorFromSentence(input_lang, pair[0])
target_tensor = tensorFromSentence(output_lang, pair[1])
return (input_tensor, target_tensor)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
teacher_forcing_ratio = 0.5
def train(input_tensor, target_tensor, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion, max_length=MAX_LENGTH):
encoder_hidden = encoder.initHidden()

encoder_optimizer.zero_grad()
decoder_optimizer.zero_grad()

input_length = input_tensor.size(0)
target_length = target_tensor.size(0)

encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)

loss = 0

for ei in range(input_length):
encoder_output, encoder_hidden = encoder(
input_tensor[ei], encoder_hidden)
encoder_outputs[ei] = encoder_output[0, 0]

decoder_input = torch.tensor([[SOS_token]], device=device)

decoder_hidden = encoder_hidden

use_teacher_forcing = True if random.random() < teacher_forcing_ratio else False

if use_teacher_forcing:
# Teacher forcing: Feed the target as the next input
for di in range(target_length):
decoder_output, decoder_hidden, decoder_attention = decoder(
decoder_input, decoder_hidden, encoder_outputs)
loss += criterion(decoder_output, target_tensor[di])
decoder_input = target_tensor[di] # Teacher forcing

else:
# Without teacher forcing: use its own predictions as the next input
for di in range(target_length):
decoder_output, decoder_hidden, decoder_attention = decoder(
decoder_input, decoder_hidden, encoder_outputs)
topv, topi = decoder_output.topk(1)
decoder_input = topi.squeeze().detach() # detach from history as input

loss += criterion(decoder_output, target_tensor[di])
if decoder_input.item() == EOS_token:
break

loss.backward()

encoder_optimizer.step()
decoder_optimizer.step()

return loss.item() / target_length
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import time
import math

def asMinutes(s):
m = math.floor(s / 60)
s -= m * 60
return '%dm %ds' % (m, s)

def timeSince(since, percent):
now = time.time()
s = now - since
es = s / (percent)
rs = es - s
return '%s (- %s)' % (asMinutes(s), asMinutes(rs))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def trainIters(encoder, decoder, n_iters, print_every=1000, plot_every=100, learning_rate=0.01):
start = time.time()
plot_losses = []
print_loss_total = 0 # Reset every print_every
plot_loss_total = 0 # Reset every plot_every

encoder_optimizer = optim.SGD(encoder.parameters(), lr=learning_rate)
decoder_optimizer = optim.SGD(decoder.parameters(), lr=learning_rate)
training_pairs = [tensorsFromPair(random.choice(pairs))
for i in range(n_iters)]
criterion = nn.NLLLoss()

for iter in range(1, n_iters + 1):
training_pair = training_pairs[iter - 1]
input_tensor = training_pair[0]
target_tensor = training_pair[1]

loss = train(input_tensor, target_tensor, encoder,
decoder, encoder_optimizer, decoder_optimizer, criterion)
print_loss_total += loss
plot_loss_total += loss

if iter % print_every == 0:
print_loss_avg = print_loss_total / print_every
print_loss_total = 0
print('%s (%d %d%%) %.4f' % (timeSince(start, iter / n_iters),
iter, iter / n_iters * 100, print_loss_avg))

if iter % plot_every == 0:
plot_loss_avg = plot_loss_total / plot_every
plot_losses.append(plot_loss_avg)
plot_loss_total = 0

showPlot(plot_losses)
1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt
plt.switch_backend('agg')
import matplotlib.ticker as ticker
import numpy as np


def showPlot(points):
plt.figure()
fig, ax = plt.subplots()
# this locator puts ticks at regular intervals
loc = ticker.MultipleLocator(base=0.2)
ax.yaxis.set_major_locator(loc)
plt.plot(points)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def evaluate(encoder, decoder, sentence, max_length=MAX_LENGTH):
with torch.no_grad():
input_tensor = tensorFromSentence(input_lang, sentence)
input_length = input_tensor.size()[0]
encoder_hidden = encoder.initHidden()

encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)

for ei in range(input_length):
encoder_output, encoder_hidden = encoder(input_tensor[ei],
encoder_hidden)
encoder_outputs[ei] += encoder_output[0, 0]

decoder_input = torch.tensor([[SOS_token]], device=device) # SOS

decoder_hidden = encoder_hidden

decoded_words = []
decoder_attentions = torch.zeros(max_length, max_length)

for di in range(max_length):
decoder_output, decoder_hidden, decoder_attention = decoder(
decoder_input, decoder_hidden, encoder_outputs)
decoder_attentions[di] = decoder_attention.data
topv, topi = decoder_output.data.topk(1)
if topi.item() == EOS_token:
decoded_words.append('<EOS>')
break
else:
decoded_words.append(output_lang.index2word[topi.item()])

decoder_input = topi.squeeze().detach()

return decoded_words, decoder_attentions[:di + 1]
1
2
3
4
5
6
7
8
9
def evaluateRandomly(encoder, decoder, n=10):
for i in range(n):
pair = random.choice(pairs)
print('>', pair[0])
print('=', pair[1])
output_words, attentions = evaluate(encoder, decoder, pair[0])
output_sentence = ' '.join(output_words)
print('<', output_sentence)
print('')
1
2
3
4
5
hidden_size = 256
encoder1 = EncoderRNN(input_lang.n_words, hidden_size).to(device)
attn_decoder1 = AttnDecoderRNN(hidden_size, output_lang.n_words, dropout_p=0.1).to(device)

trainIters(encoder1, attn_decoder1, 75000, print_every=5000)
1
evaluateRandomly(encoder1,attn_decoder,10)

92. 機械翻訳モデルの適用

91で学習したニューラル機械翻訳モデルを用い,与えられた(任意の)日本語の文を英語に翻訳するプログラムを実装せよ
単語分割をする必要があるので、MeCabを利用しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
!sudo apt-get install mecab mecab-ipadic-utf8 libmecab-dev swig
!pip install mecab-python3
import MeCab
def translate(s):
wakati = MeCab.Tagger("-Owakati")
seq = wakati.parse(s).rstrip()
seq = seq.split()
seq = [s for s in seq if s in input_lang.word2index]
seq = ' '.join(seq)
output_words, attentions = evaluate(encoder1, attn_decoder1, seq)
return ' '.join(output_words)
s = 'テスト'
print (translate(s))

93. BLEUスコアの計測

91で学習したニューラル機械翻訳モデルの品質を調べるため,評価データにおけるBLEUスコアを測定せよ.

nltkを利用します。

別の記事でBLEUスコアのスクラッチ実装しました。BLEUスコアとは? Pythonでのスクラッチ実装

1
2
3
4
5
6
7
8
9
10
11
12
13
import nltk
test_ja = 'kftt-data-1.0/data/tok/kyoto-test.ja'
test_en = 'kftt-data-1.0/data/tok/kyoto-test.en'
_, _, test_pairs = prepareData(test_ja, test_en)
sm = []
for p in test_pairs:
ref = p[1]
hyp = translate(p[0])
ref = ref.replace('.','')
hyp = hyp.replace('.','')
score = nltk.translate.bleu_score.sentence_bleu([ref.split()],hyp.split())
sm.append(score)
print (np.mean(sm))

最後に

全100問の解説に戻る

記事情報

  • 投稿日:2020年5月28日
  • 最終更新日:2020年6月7日

ABC150C Count Order

この記事で使うアルゴリズム

全探索

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/abc150/tasks/abc150_c

この問題は全探索を用いて解くことができます。

方針

制約条件から全探索で十分に間に合います。辞書順に順列を全探索し、P, Qがそれぞれ何番目に出現したかを記録すれば良いです。

itertools.permutationsに辞書順でソートされたシーケンスを渡せば、順列を辞書順に取得できるイテレータが生成されます。

コード

1
2
3
4
5
6
7
8
9
10
11
12
from itertools import permutations
N = int(input())
P = tuple(map(int, input().split()))
Q = tuple(map(int, input().split()))

for i,p in enumerate(permutations(range(1,N+1))): # (1,2,3,..)のように辞書順に並んでいるシーケンスを渡す
if p==P:
a = i
if p==Q:
b = i

print (abs(a-b))

記事情報

  • 投稿日:2020年5月28日
  • 最終更新日:2020年5月28日

ARC054B ムーアの法則

この記事で使うアルゴリズム

二分探索

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/arc054/tasks/arc054_b

方針

計算を終えるまでの時間は凸関数です。そのため三分探索により近似解を求めることができます。

left, rightに対して三等分する点m1, m2を考えます。計算を終えるまでの時間costとして、cost(m1)<cost(m2)である時、解はleftからm2の間に存在することがわかります。これらの値を十分な回数更新すれば近似解を求めることができます。

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
def cost(x):
return x + P * pow(2,-(x/1.5))
P = float(input())
left = 0
right = 10**18
for i in range(10**5):
m1 = (2*left + right) / 3
m2 = (left + 2*right) / 3
if cost(m1) < cost(m2):
right = m2
else:
left = m1
print(cost(left))

記事情報

  • 投稿日:2020年5月27日
  • 最終更新日:2020年5月27日

AOJ2013 大崎

この記事で使うアルゴリズム

累積和
いもす法

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2013

方針

いもす法を用いることで計算量O(N+K)で解くことができます。(ここではK=24*3600

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from itertools import accumulate

def time_to_seconds(time):
h, m, s = time.split(':')
return int(h) * 3600 + int(m) * 60 + int(s)

ans = []
while(1):
N = int(input())
if N==0:
break
A = [0] * 86400
for n in range(N):
begin, end = input().split()
begin = time_to_seconds(begin)
end = time_to_seconds(end)
A[begin] += 1
A[end] -= 1
cumsum = accumulate(A)
ans.append(max(cumsum))

for a in ans:
print(a)

Kが大きい場合、以下のようにソートを利用することで計算量O(NlogN)で解くこともできます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from itertools import accumulate

def time_to_seconds(time):
h, m, s = time.split(':')
return int(h) * 3600 + int(m) * 60 + int(s)

ans = []
while(1):
N = int(input())
if N==0:
break
A = []
for n in range(N):
begin, end = input().split()
begin = time_to_seconds(begin)
end = time_to_seconds(end)
A.append((begin,1))
A.append((end,-1))
A.sort()
mx = 0
cumsum = 0
for _, x in A:
cumsum += x
mx = max(mx, cumsum)
ans.append(mx)

for a in ans:
print(a)

記事情報

  • 投稿日:2020年5月26日
  • 最終更新日:2020年5月26日

JOI2008本選C ダーツ

この記事で使うアルゴリズム

二分探索

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/joi2008ho/tasks/joi2008ho_c
https://www.ioi-jp.org/joi/2007/2008-ho-prob_and_sol/2008-ho.pdf#page=6

この問題は二分探索で解くことができます。

方針

まず全探索の計算量はO(N^4)なので、最悪のケースN=1000では間に合いません。

次に単純な二分探索について考えます。ダーツを3回投げたのち、最後の1回でどこに投げれば良いかを二分探索します。これは計算量がO((N^3)logN)なので、これでも最悪のケースN=1000では間に合いません。

より計算量を削減する方法としてダーツを2本ずつにまとめて考えます。二分探索をすると、計算量は、O((N^2)log(N^2))となります。(log(N^2)=2logNなので計算量はO((N^2)logN)とも表せます)

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import bisect
N, M = map(int, input().split())
P = [0]
for n in range(N):
P.append(int(input()))
P.sort()
S = []
for p1 in P:
for p2 in P:
S.append(p1+p2)
S.sort()
ans = 0
for s in S:
if M < s:
break
i = bisect.bisect(S, M-s)-1
ans = max(s+S[i], ans)
print (ans)

記事情報

  • 投稿日:2020年5月25日
  • 最終更新日:2020年5月25日

コーティングを支える技術 感想

はじめに

書籍「コーディングを支える技術」を読了したので、この記事ではその感想を残します。

書籍の概要

プログラミング言語の概念についての解説書です。
特定の言語についての解説書ではなく、プログラミング言語そのものに関する疑問(例えば、なぜif文があるのか)を、歴史を交えながら解説しています。

想定する読者のレベル

専門書というよりは読み物ですので、ある程度のプログラミングの経験があれば読み進められると思います。

  • プログラミングの概念について勉強したい
  • 専門書の息抜きをしたい
  • 新しい言語に挑戦したい

と思っている方にはぜひ読んでいただきたいです。

目次

著者の公式ページから引用させていただきます。

  1. 言語を深く効率的に学ぶには
  2. プログラミング言語を俯瞰する
  3. 文法の誕生
  4. 処理の流れのコントロール
  5. 関数
  6. エラー処理
  7. 名前とスコープ
  8. コンテナと文字列
  9. 並行処理
  10. オブジェクトとクラス
  11. 継承によるコードの再利用

興味深かった話題

私の独断と偏見ですが、面白かった話題です。こういった話題にピンと来られた方にオススメです。

  • 決め事は言語によって異なる
    • 何が真で何が偽か
    • こうかけたほうが自然だから、そう書けるという約束にしよう
  • どの言語を学ぶべきかは誰にもわからない
  • 現代にも生きるスタックマシン
    • スタックマシン型のVMを使っている言語
  • 計算順序をどう表現する?
  • ifがなぜあるか
  • if, while, breakは制限付きのgoto
  • 関数の誕生
  • 長い変数名を付ける
  • 初期のFORTRANでの型
  • Pythonの参照カウント
  • 型推論
  • 配列と連結リスト
  • トランサクショナルメモリ
  • 言語によって違う「オブジェクト思考」の意味
  • クラスタ持つ3つの役割

最後に

非常にお勧めできる書籍です。プログラミングの概念をその成り立ちの歴史を交えてまとめており、何年たっても色褪せない内容になっていると思います。

記事情報

  • 投稿日:2020年5月25日
  • 最終更新日:2020年5月25日

ALDS1 11B 深さ優先探索

この記事で使うアルゴリズム

深さ優先探索

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_11_B&lang=ja

この問題は深さ優先探索で解くことができます。

方針

深さ優先探索はスタックや再帰で実装することができますが、今回は再帰で実装します。

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
N = int(input())
adj = [[] for i in range(N)]
for n in range(N):
V = list(map(int, input().split()))
for v in V[2:]: # u,k,v1,v2,...
adj[n].append(v-1)
d = [0] * N # 発見時刻
f = [0] * N # 完了時刻

def dfs(v, t):
t+=1 # 発見したらインクリメント
d[v] = t
for next in adj[v]:
if d[next] == 0: # 未発見なら
t = dfs(next, t)
t+=1 # 完了してもインクリメント
f[v] = t
return t

t = 0
for n in range(N):
if d[n]==0: # 未発見なら
t = dfs(n,t)
print (n+1,d[n],f[n])

記事情報

  • 投稿日:2020年5月24日
  • 最終更新日:2020年5月24日

scikit-learnでよく使う便利な機能まとめ

scikit-learnでよく使う便利な機能をまとめました。

train_test_split

データを学習データとテストデータに分離することができます。

1
2
3
4
5
6
7
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
breast_cancer = load_breast_cancer()
X = breast_cancer.data
y = breast_cancer.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

引数

  • test_size
    テストデータのサイズを指定します。引数としてintを与えると絶対数、floatを与えると比率と見なされます。同様にtrain_sizeを指定することもできます。

  • shuffle
    分割の前にデータの順番をランダムにシャッフルします。これはデータの順番に意味がある場合に、不適切な検証がなされることを防ぐための機能です。

  • random_state
    前述のshuffleのseed値です。

LogisticRegression

ロジスティック回帰モデルの学習や推論ができます。

1
2
3
4
5
6
7
8
9
10
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
breast_cancer = load_breast_cancer()
X = breast_cancer.data
y = breast_cancer.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
clf = LogisticRegression()
clf.fit(X_train, y_train)
clf.predict(X_test)

メソッド

  • fit
    学習します。
  • predict
    推論します。クラスラベルを返却します。
  • predict_proba
    推論します。クラスラベルごとに確率を返却します。

cross_val_score

交差検証(クロスバリデーション)を行い、そのスコアを返却します。

1
2
from sklearn.model_selection import cross_val_score
scores = cross_val_score(clf, X, y, cv=5, scoring='accuracy')

引数

  • cv
    分割数、cross validation generator、イテラブルを引数にとります。
  • scoring
    スコアリングの指標を指定します。詳細はこのページに記述があリマス。

plot_roc_curve

ROC曲線をプロットします。matplotlibのimportが必要です。

1
2
3
4
from sklearn.metrics import plot_roc_curve
import matplotlib.pyplot as plt
ax = plt.gca()
plot_roc_curve(clf, X_test, y_test, ax=ax)

StandardScaler

標準化を行います。標準化とは、平均を0に、分散を1に変換することです。

1
2
3
4
5
6
from sklearn import preprocessing
scaler = preprocessing.StandardScaler().fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
clf = LogisticRegression()
clf.fit(X_train_scaled, y_train)

メソッド

  • fit
    平均と標準偏差を計算します。
  • transform
    fitで得られた統計量を用いて、標準化を行います。
  • fit_transform
    fittransformを同時に行います。

PCA

主成分分析を行います。

1
2
3
4
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit_transform(X_train)
pca.fit(X_test)

メソッド

  • fit
    transformの準備をします。(分散共分散行列の特異値分解)
  • transform
    fitで得られた値を元に、次元削減を行います。
  • fit_transform
    fittransformを同時に行います。

属性

  • explained_variance_
    特異値
  • explained_variance_ratio_
    寄与率
  • pca.components_
    左特異ベクトル

accuracy

正解率を計算します。

1
2
from sklearn.metrics import accuracy_score
accuracy_score(y_test, clf.predict(X_test))

その他の指標

同様のインターフェイスで主要な指標は実装されています。

  • precision_score
  • recall_score
  • f1_score

上記のような複数の指標を確認したい際はclassification_reportが便利です。

1
2
from sklearn.metrics import classification_report
classification_report(y_test, clf.predict(X_test))

引数

  • sample_weight
    スコアに重み付けをすることができます。

終わりに

これらの機能を実践的に利用した記事がありますので併せてご覧ください。

記事情報

  • 投稿日:2020年5月24日
  • 最終更新日:2020年5月24日

まとめ記事の一覧

このページでは、このブログのまとめ記事を一覧にしています。

言語処理100本ノック2020 解答例一覧

言語処理100本ノックは東北大学が公開している自然言語処理の問題集です。

とても良質なコンテンツで企業の研修や勉強会で使われています。

そんな言語処理100本ノックが2020年に改定されてました。昨今の状況を鑑みて、深層ニューラルネットワークに関する問題が追加されました。(その他にも細かい変更があります)

このまとめでは、言語処理100本ノック2020をPythonで解きました。他にも色々な解法があると思うので、一つの解答例としてご活用ください!

分野別 初中級者が解くべき過去問精選100問

競技プログラミングのまとめ記事です。レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】2-3節にまとめられている100問をPythonで解いています。

連載「scikit-learnで学ぶ機械学習」

scikit-learnのユーザガイドで整理されている構成に準拠して、8つのテーマを扱います。

  • Supervised learning
  • Unsupervised learning
  • Model selection and evaluation
  • Inspection
  • Visualizations
  • Dataset transformations
  • Dataset loading utilities
  • Computing with scikit-learn

scikit-learnはとにかくアルゴリズムが豊富で、主要なものはほぼ揃っていると考えて良いです。そのため、教科書に出てくるような概念は、大体scikit-learnで実装されているので、scikit-learnも絶好の教材というわけです。

連載「ゼロから始める機械学習」

全6回の機械学習の超入門記事です。以下のような方にオススメです。

  • 機械学習の基本を知りたい
  • 機械学習の実装を少しでもできるようにしたい
  • Python学習のゴールとして機械学習を使えるようにしたい
  • エンジニアとしての技術を広げるために、機械学習の概略を理解したい
  • 機械学習を勉強したことはあるが、ライブラリを使っただけで結局よくわからなかった

記事情報

  • 投稿日:2020年5月23日
  • 最終更新日:2020年5月23日

ABC139D ModSum

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/abc139/tasks/abc139_d

この問題は簡単な数学的考察で解くことができます。

方針

まず答えの上限について考えます。ある数をnで割った時の余りは最大でn-1であるため、解は最大で0 + 1 + 2 +..+ N-1となることが分かります。

実は任意のNについてそのような解を、以下のようなパターンで実現できます。
Nmod1 + 1mod2 + 2mod3 + (N-1)modN

結局、解は0 + 1 + 2 +..+ N-1を求めれば良いということになります。これはシグマ公式によりO(1)で求めることができます。

コード

1
2
N = int(input())
print (N*(N-1)//2)

記事情報

  • 投稿日:2020年5月22日
  • 最終更新日:2020年5月22日