言語処理100本ノック2020 第9章 RNN,CNN

はじめに

言語処理100本ノック2020

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

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

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

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

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

全100問の解説に戻る

80. ID番号への変換

問題51で構築した学習データ中の単語にユニークなID番号を付与したい.学習データ中で最も頻出する単語に1,2番目に頻出する単語に2,……といった方法で,学習データ中で2回以上出現する単語にID番号を付与せよ.そして,与えられた単語列に対して,ID番号の列を返す関数を実装せよ.ただし,出現頻度が2回未満の単語のID番号はすべて0とせよ.

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
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer
train = pd.read_csv(base+'train.txt', header=None, sep='\t')
valid = pd.read_csv(base+'valid.txt', header=None, sep='\t')
test = pd.read_csv(base+'test.txt', header=None, sep='\t')
vectorizer = CountVectorizer(min_df=2)
train_title = train.iloc[:,1].str.lower()
cnt = vectorizer.fit_transform(train_title).toarray()
sm = cnt.sum(axis=0)
idx = np.argsort(sm)[::-1]
words = np.array(vectorizer.get_feature_names())[idx]
d = dict()
for i in range(len(words)):
d[words[i]] = i+1
def get_id(sentence):
r = []
for word in sentence:
r.append(d.get(word,0))
return r

def df2id(df):
ids = []
for i in df.iloc[:,1].str.lower():
ids.append(get_id(i.split()))
return ids

X_train = df2id(train)
X_valid = df2id(valid)
X_test = df2id(test)

81. RNNによる予測

D番号で表現された単語列x=(x1,x2,…,xT)がある.ただし,Tは単語列の長さ,xt∈ℝVは単語のID番号のone-hot表記である(Vは単語の総数である).再帰型ニューラルネットワーク(RNN: Recurrent Neural Network)を用い,単語列xからカテゴリyを予測するモデルとして,次式を実装せよ.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
dw = 300
dh = 50
class RNN(torch.nn.Module):
def __init__(self):
super().__init__()
self.emb = torch.nn.Embedding(len(words)+1,dw)
self.rnn = torch.nn.RNN(dw,dh,batch_first=True)
self.linear = torch.nn.Linear(dh,4)
self.softmax = torch.nn.Softmax()
def forward(self, x, h=None):
x = self.emb(x)
y, h = self.rnn(x, h)
y = y[:,-1,:] # 最後のステップ
y = self.linear(y)
y = self.softmax(y)
return y

82. 確率的勾配降下法による学習

確率的勾配降下法(SGD: Stochastic Gradient Descent)を用いて,問題81で構築したモデルを学習せよ.訓練データ上の損失と正解率,評価データ上の損失と正解率を表示しながらモデルを学習し,適当な基準(例えば10エポックなど)で終了させよ.

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import torch
from torch.utils.data import TensorDataset, DataLoader
from torch.utils.tensorboard import SummaryWriter
%load_ext tensorboard
!rm -rf ./runs
%tensorboard --logdir ./runs
writer = SummaryWriter()

max_len = 10
dw = 300
dh = 50
n_vocab = len(words) + 2
PAD = len(words) + 1

class RNN(torch.nn.Module):
def __init__(self):
super().__init__()
self.emb = torch.nn.Embedding(n_vocab,dw,padding_idx=PAD)
self.rnn = torch.nn.RNN(dw,dh,batch_first=True)
self.linear = torch.nn.Linear(dh,4)
self.softmax = torch.nn.Softmax()
def forward(self, x, h=None):
x = self.emb(x)
y, h = self.rnn(x, h)
y = y[:,-1,:] # 最後のステップ
y = self.linear(y)
# y = self.softmax(y) # torch.nn.CrossEntropyLoss()がsoftmaxは含む
return y

def list2tensor(data, max_len):
new = []
for d in data:
if len(d) > max_len:
d = d[:max_len]
else:
d += [PAD] * (max_len - len(d))
new.append(d)
return torch.tensor(new, dtype=torch.int64)

def accuracy(pred, label):
pred = np.argmax(pred.data.numpy(), axis=1)
label = label.data.numpy()
return (pred == label).mean()



train = pd.read_csv(base+'train.txt', header=None, sep='\t')
valid = pd.read_csv(base+'valid.txt', header=None, sep='\t')
test = pd.read_csv(base+'test.txt', header=None, sep='\t')

X_train = df2id(train)
X_valid = df2id(valid)
X_test = df2id(test)

X_train = list2tensor(X_train,max_len)
X_valid = list2tensor(X_valid,max_len)
X_test = list2tensor(X_test,max_len)

y_train = np.loadtxt(base+'y_train.txt')
y_train = torch.tensor(y_train, dtype=torch.int64)
y_valid = np.loadtxt(base+'y_valid.txt')
y_valid = torch.tensor(y_valid, dtype=torch.int64)
y_test = np.loadtxt(base+'y_test.txt')
y_test = torch.tensor(y_test, dtype=torch.int64)


model = RNN()
ds = TensorDataset(X_train, y_train)
# DataLoaderを作成
loader = DataLoader(ds, batch_size=1, shuffle=True)
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)

for epoch in range(2):
for xx, yy in loader:
y_pred = model(xx)
loss = loss_fn(y_pred, yy)
optimizer.zero_grad()
loss.backward()
optimizer.step()
with torch.no_grad():
y_pred = model(X_train)
loss = loss_fn(y_pred, y_train)
writer.add_scalar('Loss/train', loss, epoch)
writer.add_scalar('Accuracy/train', accuracy(y_pred,y_train), epoch)
print (accuracy(y_pred,y_train))

y_pred = model(X_valid)
loss = loss_fn(y_pred, y_valid)
writer.add_scalar('Loss/valid', loss, epoch)
writer.add_scalar('Accuracy/valid', accuracy(y_pred,y_valid), epoch)
print (accuracy(y_pred,y_valid))

83. ミニバッチ化・GPU上での学習

問題82のコードを改変し,B事例ごとに損失・勾配を計算して学習を行えるようにせよ(Bの値は適当に選べ).また,GPU上で学習を実行せよ.

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import torch
from torch.utils.data import TensorDataset, DataLoader
from torch.utils.tensorboard import SummaryWriter
%load_ext tensorboard
!rm -rf ./runs
%tensorboard --logdir ./runs
writer = SummaryWriter()

max_len = 10
dw = 300
dh = 50
n_vocab = len(words) + 2
PAD = len(words) + 1

class RNN(torch.nn.Module):
def __init__(self):
super().__init__()
self.emb = torch.nn.Embedding(n_vocab,dw,padding_idx=PAD)
self.rnn = torch.nn.RNN(dw,dh,batch_first=True)
self.linear = torch.nn.Linear(dh,4)
self.softmax = torch.nn.Softmax()
def forward(self, x, h=None):
x = self.emb(x)
y, h = self.rnn(x, h)
y = y[:,-1,:] # 最後のステップ
y = self.linear(y)
# y = self.softmax(y) # torch.nn.CrossEntropyLoss()がsoftmaxは含む
return y

def list2tensor(data, max_len):
new = []
for d in data:
if len(d) > max_len:
d = d[:max_len]
else:
d += [PAD] * (max_len - len(d))
new.append(d)
return torch.tensor(new, dtype=torch.int64)

def accuracy(pred, label):
pred = np.argmax(pred.data.to('cpu').numpy(), axis=1)
label = label.data.to('cpu').numpy()
return (pred == label).mean()

train = pd.read_csv(base+'train.txt', header=None, sep='\t')
valid = pd.read_csv(base+'valid.txt', header=None, sep='\t')
test = pd.read_csv(base+'test.txt', header=None, sep='\t')

X_train = df2id(train)
X_valid = df2id(valid)
X_test = df2id(test)

X_train = list2tensor(X_train,max_len)
X_valid = list2tensor(X_valid,max_len)
X_test = list2tensor(X_test,max_len)

y_train = np.loadtxt(base+'y_train.txt')
y_train = torch.tensor(y_train, dtype=torch.int64)
y_valid = np.loadtxt(base+'y_valid.txt')
y_valid = torch.tensor(y_valid, dtype=torch.int64)
y_test = np.loadtxt(base+'y_test.txt')
y_test = torch.tensor(y_test, dtype=torch.int64)


model = RNN()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
ds = TensorDataset(X_train.to(device), y_train.to(device))
# DataLoaderを作成
loader = DataLoader(ds, batch_size=1024, shuffle=True)
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)

for epoch in range(10):
for xx, yy in loader:
y_pred = model(xx)
loss = loss_fn(y_pred, yy)
optimizer.zero_grad()
loss.backward()
optimizer.step()
with torch.no_grad():
y_pred = model(X_train.to(device))
loss = loss_fn(y_pred, y_train.to(device))
writer.add_scalar('Loss/train', loss, epoch)
writer.add_scalar('Accuracy/train', accuracy(y_pred,y_train), epoch)
print (accuracy(y_pred,y_train))

y_pred = model(X_valid.to(device))
loss = loss_fn(y_pred, y_valid.to(device))
writer.add_scalar('Loss/valid', loss, epoch)
writer.add_scalar('Accuracy/valid', accuracy(y_pred,y_valid), epoch)
print (accuracy(y_pred,y_valid))

84. 単語ベクトルの導入

事前学習済みの単語ベクトル(例えば,Google Newsデータセット(約1,000億単語)での学習済み単語ベクトル)で単語埋め込みemb(x)を初期化し,学習せよ.

model.emb.weight(変数名は適宜読み換えてください)のようにアクセスできます。テンソルをパラメータとして扱いたい場合はtorch.nn.Parameter()に通す必要があります。

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import torch
from torch.utils.data import TensorDataset, DataLoader
from torch.utils.tensorboard import SummaryWriter

max_len = 10
dw = 300
dh = 50
n_vocab = len(words) + 2
PAD = len(words) + 1

class RNN(torch.nn.Module):
def __init__(self):
super().__init__()
self.emb = torch.nn.Embedding(n_vocab,dw,padding_idx=PAD)
self.rnn = torch.nn.RNN(dw,dh,batch_first=True)
self.linear = torch.nn.Linear(dh,4)
self.softmax = torch.nn.Softmax()
def forward(self, x, h=None):
x = self.emb(x)
y, h = self.rnn(x, h)
y = y[:,-1,:] # 最後のステップ
y = self.linear(y)
# y = self.softmax(y) # torch.nn.CrossEntropyLoss()がsoftmaxは含む
return y

def list2tensor(data, max_len):
new = []
for d in data:
if len(d) > max_len:
d = d[:max_len]
else:
d += [PAD] * (max_len - len(d))
new.append(d)
return torch.tensor(new, dtype=torch.int64)

def accuracy(pred, label):
pred = np.argmax(pred.data.to('cpu').numpy(), axis=1)
label = label.data.to('cpu').numpy()
return (pred == label).mean()


train = pd.read_csv(base+'train.txt', header=None, sep='\t')
valid = pd.read_csv(base+'valid.txt', header=None, sep='\t')
test = pd.read_csv(base+'test.txt', header=None, sep='\t')

X_train = df2id(train)
X_valid = df2id(valid)
X_test = df2id(test)

X_train = list2tensor(X_train,max_len)
X_valid = list2tensor(X_valid,max_len)
X_test = list2tensor(X_test,max_len)

y_train = np.loadtxt(base+'y_train.txt')
y_train = torch.tensor(y_train, dtype=torch.int64)
y_valid = np.loadtxt(base+'y_valid.txt')
y_valid = torch.tensor(y_valid, dtype=torch.int64)
y_test = np.loadtxt(base+'y_test.txt')
y_test = torch.tensor(y_test, dtype=torch.int64)

model = RNN()
#print (model.emb.weight)
for k,v in d.items():
if k in w2v.vocab:
#v = np.random.randint(1,PAD) #ランダムな単語ベクトルでも効果があるか
model.emb.weight[v] = torch.tensor(w2v[k], dtype=torch.float32)
#print (model.emb.weight)
model.emb.weight = torch.nn.Parameter(model.emb.weight)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

ds = TensorDataset(X_train.to(device), y_train.to(device))
# DataLoaderを作成
loader = DataLoader(ds, batch_size=1024, shuffle=True)
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)

for epoch in range(10):
for xx, yy in loader:
y_pred = model(xx)
loss = loss_fn(y_pred, yy)
optimizer.zero_grad()
loss.backward()
optimizer.step()
with torch.no_grad():
y_pred = model(X_train.to(device))
loss = loss_fn(y_pred, y_train.to(device))
writer.add_scalar('Loss/train', loss, epoch)
writer.add_scalar('Accuracy/train', accuracy(y_pred,y_train), epoch)
print (accuracy(y_pred,y_train))

y_pred = model(X_valid.to(device))
loss = loss_fn(y_pred, y_valid.to(device))
writer.add_scalar('Loss/valid', loss, epoch)
writer.add_scalar('Accuracy/valid', accuracy(y_pred,y_valid), epoch)
print (accuracy(y_pred,y_valid))
#print (model.emb.weight)

85. 双方向RNN・多層化

順方向と逆方向のRNNの両方を用いて入力テキストをエンコードし,モデルを学習せよ.

torch.nn.RNN(bidirectional=True)とすれば良いです。

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import torch
from torch.utils.data import TensorDataset, DataLoader
from torch.utils.tensorboard import SummaryWriter

max_len = 10
dw = 300
dh = 50
n_vocab = len(words) + 2
PAD = len(words) + 1

class RNN(torch.nn.Module):
def __init__(self):
super().__init__()
self.emb = torch.nn.Embedding(n_vocab,dw,padding_idx=PAD)
self.rnn1 = torch.nn.RNN(dw,dh,bidirectional=True,batch_first=True)
self.rnn2 = torch.nn.RNN(2*dh,dh,bidirectional=True,batch_first=True)
self.rnn3 = torch.nn.RNN(2*dh,dh,bidirectional=True,batch_first=True)
self.linear = torch.nn.Linear(2*dh,4)
self.softmax = torch.nn.Softmax()
def forward(self, x, h=None):
x = self.emb(x)
y, h = self.rnn1(x, h)
y, h = self.rnn2(y, h)
y, h = self.rnn3(y, h)
y = y[:,-1,:] # 最後のステップ
y = self.linear(y)
# y = self.softmax(y) # torch.nn.CrossEntropyLoss()がsoftmaxは含む
return y

def list2tensor(data, max_len):
new = []
for d in data:
if len(d) > max_len:
d = d[:max_len]
else:
d += [PAD] * (max_len - len(d))
new.append(d)
return torch.tensor(new, dtype=torch.int64)

def accuracy(pred, label):
pred = np.argmax(pred.data.to('cpu').numpy(), axis=1)
label = label.data.to('cpu').numpy()
return (pred == label).mean()


train = pd.read_csv(base+'train.txt', header=None, sep='\t')
valid = pd.read_csv(base+'valid.txt', header=None, sep='\t')
test = pd.read_csv(base+'test.txt', header=None, sep='\t')

X_train = df2id(train)
X_valid = df2id(valid)
X_test = df2id(test)

X_train = list2tensor(X_train,max_len)
X_valid = list2tensor(X_valid,max_len)
X_test = list2tensor(X_test,max_len)

y_train = np.loadtxt(base+'y_train.txt')
y_train = torch.tensor(y_train, dtype=torch.int64)
y_valid = np.loadtxt(base+'y_valid.txt')
y_valid = torch.tensor(y_valid, dtype=torch.int64)
y_test = np.loadtxt(base+'y_test.txt')
y_test = torch.tensor(y_test, dtype=torch.int64)

model = RNN()
#print (model.emb.weight)
for k,v in d.items():
if k in w2v.vocab:
#v = np.random.randint(1,PAD) #ランダムな単語ベクトルでも効果があるか
model.emb.weight[v] = torch.tensor(w2v[k], dtype=torch.float32)
#print (model.emb.weight)
model.emb.weight = torch.nn.Parameter(model.emb.weight)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

ds = TensorDataset(X_train.to(device), y_train.to(device))
# DataLoaderを作成
loader = DataLoader(ds, batch_size=1024, shuffle=True)
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)

for epoch in range(10):
for xx, yy in loader:
y_pred = model(xx)
loss = loss_fn(y_pred, yy)
optimizer.zero_grad()
loss.backward()
optimizer.step()
with torch.no_grad():
y_pred = model(X_train.to(device))
loss = loss_fn(y_pred, y_train.to(device))
writer.add_scalar('Loss/train', loss, epoch)
writer.add_scalar('Accuracy/train', accuracy(y_pred,y_train), epoch)
print (accuracy(y_pred,y_train))

y_pred = model(X_valid.to(device))
loss = loss_fn(y_pred, y_valid.to(device))
writer.add_scalar('Loss/valid', loss, epoch)
writer.add_scalar('Accuracy/valid', accuracy(y_pred,y_valid), epoch)
print (accuracy(y_pred,y_valid))

86. 畳み込みニューラルネットワーク (CNN)

ID番号で表現された単語列x=(x1,x2,…,xT)がある.ただし,Tは単語列の長さ,xt∈ℝVは単語のID番号のone-hot表記である(Vは単語の総数である).畳み込みニューラルネットワーク(CNN: Convolutional Neural Network)を用い,単語列xからカテゴリyを予測するモデルを実装せよ.

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import torch
from torch.utils.data import TensorDataset, DataLoader
from torch.utils.tensorboard import SummaryWriter

max_len = 10
dw = 300
dh = 50
n_vocab = len(words) + 2
PAD = len(words) + 1

class CNN(torch.nn.Module):
def __init__(self):
super().__init__()
self.emb = torch.nn.Embedding(n_vocab,dw,padding_idx=PAD)
self.conv = torch.nn.Conv1d(dw,dh,3,padding=1) # in_channels:dw, out_channels: dh
self.relu = torch.nn.ReLU()
self.pool = torch.nn.MaxPool1d(max_len)
self.linear = torch.nn.Linear(dh,4)
self.softmax = torch.nn.Softmax()
def forward(self, x, h=None):
x = self.emb(x)
x = x.view(x.shape[0], x.shape[2], x.shape[1])
x = self.conv(x)
x = self.relu(x)
x = x.view(x.shape[0], x.shape[1], x.shape[2])
x = self.pool(x)
x = x.view(x.shape[0], x.shape[1])
y = self.linear(x)
y = self.softmax(y) # torch.nn.CrossEntropyLoss()がsoftmaxは含む
return y

def list2tensor(data, max_len):
new = []
for d in data:
if len(d) > max_len:
d = d[:max_len]
else:
d += [PAD] * (max_len - len(d))
new.append(d)
return torch.tensor(new, dtype=torch.int64)

def accuracy(pred, label):
pred = np.argmax(pred.data.to('cpu').numpy(), axis=1)
label = label.data.to('cpu').numpy()
return (pred == label).mean()


train = pd.read_csv(base+'train.txt', header=None, sep='\t')
valid = pd.read_csv(base+'valid.txt', header=None, sep='\t')
test = pd.read_csv(base+'test.txt', header=None, sep='\t')

X_train = df2id(train)
X_valid = df2id(valid)
X_test = df2id(test)

X_train = list2tensor(X_train,max_len)
X_valid = list2tensor(X_valid,max_len)
X_test = list2tensor(X_test,max_len)

y_train = np.loadtxt(base+'y_train.txt')
y_train = torch.tensor(y_train, dtype=torch.int64)
y_valid = np.loadtxt(base+'y_valid.txt')
y_valid = torch.tensor(y_valid, dtype=torch.int64)
y_test = np.loadtxt(base+'y_test.txt')
y_test = torch.tensor(y_test, dtype=torch.int64)

model = CNN()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
with torch.no_grad():
y_pred = model(X_train.to(device))

87. 確率的勾配降下法によるCNNの学習

確率的勾配降下法(SGD: Stochastic Gradient Descent)を用いて,問題86で構築したモデルを学習せよ.訓練データ上の損失と正解率,評価データ上の損失と正解率を表示しながらモデルを学習し,適当な基準(例えば10エポックなど)で終了させよ.

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import torch
from torch.utils.data import TensorDataset, DataLoader
from torch.utils.tensorboard import SummaryWriter

max_len = 10
dw = 300
dh = 50
n_vocab = len(words) + 2
PAD = len(words) + 1

class CNN(torch.nn.Module):
def __init__(self):
super().__init__()
self.emb = torch.nn.Embedding(n_vocab,dw,padding_idx=PAD)
self.conv = torch.nn.Conv1d(dw,dh,3,padding=1) # in_channels:dw, out_channels: dh
self.relu = torch.nn.ReLU()
self.pool = torch.nn.MaxPool1d(max_len)
self.linear = torch.nn.Linear(dh,4)
self.softmax = torch.nn.Softmax()
def forward(self, x, h=None):
x = self.emb(x)
x = x.view(x.shape[0], x.shape[2], x.shape[1])
x = self.conv(x)
x = self.relu(x)
x = x.view(x.shape[0], x.shape[1], x.shape[2])
x = self.pool(x)
x = x.view(x.shape[0], x.shape[1])
y = self.linear(x)
return y

def list2tensor(data, max_len):
new = []
for d in data:
if len(d) > max_len:
d = d[:max_len]
else:
d += [PAD] * (max_len - len(d))
new.append(d)
return torch.tensor(new, dtype=torch.int64)

def accuracy(pred, label):
pred = np.argmax(pred.data.to('cpu').numpy(), axis=1)
label = label.data.to('cpu').numpy()
return (pred == label).mean()


train = pd.read_csv(base+'train.txt', header=None, sep='\t')
valid = pd.read_csv(base+'valid.txt', header=None, sep='\t')
test = pd.read_csv(base+'test.txt', header=None, sep='\t')

X_train = df2id(train)
X_valid = df2id(valid)
X_test = df2id(test)

X_train = list2tensor(X_train,max_len)
X_valid = list2tensor(X_valid,max_len)
X_test = list2tensor(X_test,max_len)

y_train = np.loadtxt(base+'y_train.txt')
y_train = torch.tensor(y_train, dtype=torch.int64)
y_valid = np.loadtxt(base+'y_valid.txt')
y_valid = torch.tensor(y_valid, dtype=torch.int64)
y_test = np.loadtxt(base+'y_test.txt')
y_test = torch.tensor(y_test, dtype=torch.int64)

model = CNN()
#print (model.emb.weight)
for k,v in d.items():
if k in w2v.vocab:
#v = np.random.randint(1,PAD) #ランダムな単語ベクトルでも効果があるか
model.emb.weight[v] = torch.tensor(w2v[k], dtype=torch.float32)
#print (model.emb.weight)

model.emb.weight = torch.nn.Parameter(model.emb.weight)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

ds = TensorDataset(X_train.to(device), y_train.to(device))
# DataLoaderを作成
loader = DataLoader(ds, batch_size=1024, shuffle=True)
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-1)
for epoch in range(10):
for xx, yy in loader:
y_pred = model(xx)
loss = loss_fn(y_pred, yy)
optimizer.zero_grad()
loss.backward()
optimizer.step()
with torch.no_grad():
y_pred = model(X_train.to(device))
loss = loss_fn(y_pred, y_train.to(device))
writer.add_scalar('Loss/train', loss, epoch)
writer.add_scalar('Accuracy/train', accuracy(y_pred,y_train), epoch)
print (accuracy(y_pred,y_train))

y_pred = model(X_valid.to(device))
loss = loss_fn(y_pred, y_valid.to(device))
writer.add_scalar('Loss/valid', loss, epoch)
writer.add_scalar('Accuracy/valid', accuracy(y_pred,y_valid), epoch)
print (accuracy(y_pred,y_valid))

88. パラメータチューニング

問題85や問題87のコードを改変し,ニューラルネットワークの形状やハイパーパラメータを調整しながら,高性能なカテゴリ分類器を構築せよ.
optimizerAdamに変更しました。

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import torch
from torch.utils.data import TensorDataset, DataLoader
from torch.utils.tensorboard import SummaryWriter

max_len = 10
dw = 300
dh = 50
n_vocab = len(words) + 2
PAD = len(words) + 1

class CNN(torch.nn.Module):
def __init__(self):
super().__init__()
self.emb = torch.nn.Embedding(n_vocab,dw,padding_idx=PAD)
self.conv = torch.nn.Conv1d(dw,dh,3,padding=1) # in_channels:dw, out_channels: dh
self.relu = torch.nn.ReLU()
self.pool = torch.nn.MaxPool1d(max_len)
self.linear = torch.nn.Linear(dh,4)
self.softmax = torch.nn.Softmax()
def forward(self, x, h=None):
x = self.emb(x)
x = x.view(x.shape[0], x.shape[2], x.shape[1])
x = self.conv(x)
x = self.relu(x)
x = x.view(x.shape[0], x.shape[1], x.shape[2])
x = self.pool(x)
x = x.view(x.shape[0], x.shape[1])
y = self.linear(x)
return y

def list2tensor(data, max_len):
new = []
for d in data:
if len(d) > max_len:
d = d[:max_len]
else:
d += [PAD] * (max_len - len(d))
new.append(d)
return torch.tensor(new, dtype=torch.int64)

def accuracy(pred, label):
pred = np.argmax(pred.data.to('cpu').numpy(), axis=1)
label = label.data.to('cpu').numpy()
return (pred == label).mean()


train = pd.read_csv(base+'train.txt', header=None, sep='\t')
valid = pd.read_csv(base+'valid.txt', header=None, sep='\t')
test = pd.read_csv(base+'test.txt', header=None, sep='\t')

X_train = df2id(train)
X_valid = df2id(valid)
X_test = df2id(test)

X_train = list2tensor(X_train,max_len)
X_valid = list2tensor(X_valid,max_len)
X_test = list2tensor(X_test,max_len)

y_train = np.loadtxt(base+'y_train.txt')
y_train = torch.tensor(y_train, dtype=torch.int64)
y_valid = np.loadtxt(base+'y_valid.txt')
y_valid = torch.tensor(y_valid, dtype=torch.int64)
y_test = np.loadtxt(base+'y_test.txt')
y_test = torch.tensor(y_test, dtype=torch.int64)

model = CNN()
#print (model.emb.weight)
for k,v in d.items():
if k in w2v.vocab:
#v = np.random.randint(1,PAD) #ランダムな単語ベクトルでも効果があるか
model.emb.weight[v] = torch.tensor(w2v[k], dtype=torch.float32)
#print (model.emb.weight)

model.emb.weight = torch.nn.Parameter(model.emb.weight)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

ds = TensorDataset(X_train.to(device), y_train.to(device))
# DataLoaderを作成
loader = DataLoader(ds, batch_size=1024, shuffle=True)
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
for epoch in range(50):
for xx, yy in loader:
y_pred = model(xx)
loss = loss_fn(y_pred, yy)
optimizer.zero_grad()
loss.backward()
optimizer.step()
with torch.no_grad():
y_pred = model(X_train.to(device))
loss = loss_fn(y_pred, y_train.to(device))
writer.add_scalar('Loss/train', loss, epoch)
writer.add_scalar('Accuracy/train', accuracy(y_pred,y_train), epoch)
print (accuracy(y_pred,y_train))

y_pred = model(X_valid.to(device))
loss = loss_fn(y_pred, y_valid.to(device))
writer.add_scalar('Loss/valid', loss, epoch)
writer.add_scalar('Accuracy/valid', accuracy(y_pred,y_valid), epoch)
print (accuracy(y_pred,y_valid))

89. 事前学習済み言語モデルからの転移学習

事前学習済み言語モデル(例えばBERTなど)を出発点として,ニュース記事見出しをカテゴリに分類するモデルを構築せよ.

BERTを転移学習させます。BERTを利用するためにライブラリtransformersを利用しました。BERTについては以下のページを参考にしてください。
BERTの理解に役立つ資料まとめ

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
!pip install transformers
import torch
from torch.utils.data import TensorDataset, DataLoader
from torch.utils.tensorboard import SummaryWriter
from transformers import *
import torch.nn as nn
import torch.nn.functional as F

max_len = 15
PAD = 0
n_unit = 768

tokenizer_class = BertTokenizer
tokenizer = tokenizer_class.from_pretrained('bert-base-uncased')

def dfs_freeze(model):
for name, child in model.named_children():
for param in child.parameters():
param.requires_grad = False
dfs_freeze(child)


class BertClassifier(nn.Module):
def __init__(self, n_classes=4):
super(BertClassifier, self).__init__()
self.bert_model = BertModel.from_pretrained('bert-base-uncased')
self.fc = nn.Linear(n_unit, n_classes)

def forward(self, ids):
seg_ids = torch.zeros_like(ids) # 全て同一セグメントとみなす
attention_mask = (ids > 0)
last_hidden_state, _ = self.bert_model(input_ids=ids, token_type_ids=seg_ids, attention_mask=attention_mask)
x = last_hidden_state[:,0,:] # CLSトークン
logit = self.fc(x.view(-1,n_unit))
return logit


def list2tensor(data, max_len):
new = []
for d in data:
if len(d) > max_len:
d = d[:max_len]
else:
d += [PAD] * (max_len - len(d))
new.append(d)
return torch.tensor(new, dtype=torch.int64)

def accuracy(pred, label):
pred = np.argmax(pred.data.to('cpu').numpy(), axis=1)
label = label.data.to('cpu').numpy()
return (pred == label).mean()

def df2id(df):
tokenized = df[1].apply((lambda x: tokenizer.encode(x, add_special_tokens=True)))
return tokenized

train = pd.read_csv(base+'train.txt', header=None, sep='\t')
valid = pd.read_csv(base+'valid.txt', header=None, sep='\t')
test = pd.read_csv(base+'test.txt', header=None, sep='\t')

X_train = df2id(train)
X_valid = df2id(valid)
X_test = df2id(test)

X_train = list2tensor(X_train,max_len)
X_valid = list2tensor(X_valid,max_len)
X_test = list2tensor(X_test,max_len)

y_train = np.loadtxt(base+'y_train.txt')
y_train = torch.tensor(y_train, dtype=torch.int64)
y_valid = np.loadtxt(base+'y_valid.txt')
y_valid = torch.tensor(y_valid, dtype=torch.int64)
y_test = np.loadtxt(base+'y_test.txt')
y_test = torch.tensor(y_test, dtype=torch.int64)
model = BertClassifier()

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

dfs_freeze(model)
model.fc.requires_grad_(True)

ds = TensorDataset(X_train.to(device), y_train.to(device))
# DataLoaderを作成
loader = DataLoader(ds, batch_size=1024, shuffle=True)
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
for epoch in range(30):
print(epoch)
for xx, yy in loader:
y_pred = model(xx)
loss = loss_fn(y_pred, yy)
optimizer.zero_grad()
loss.backward()
optimizer.step()
with torch.no_grad():
y_pred = model(X_train.to(device))
loss = loss_fn(y_pred, y_train.to(device))
writer.add_scalar('Loss/train', loss, epoch)
writer.add_scalar('Accuracy/train', accuracy(y_pred,y_train), epoch)
print (accuracy(y_pred,y_train))

y_pred = model(X_valid.to(device))
loss = loss_fn(y_pred, y_valid.to(device))
writer.add_scalar('Loss/valid', loss, epoch)
writer.add_scalar('Accuracy/valid', accuracy(y_pred,y_valid), epoch)
print (accuracy(y_pred,y_valid))

最後に

全100問の解説に戻る

記事情報

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

DPL1A コイン問題

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

動的計画法

はじめに

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

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

問題

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

この問題は動的計画法を用いて解くことができます。

方針

dp[m][n]を考えます。このリストは総額がnとなるようにm番目までのコインを選んだ時の枚数の最小値を表します。

コード

DPL1C ナップザック問題とほとんど同じコードになります。
最大値ではなく最小値を更新していく点に注意してください。

貰うDP

1
2
3
4
5
                  dp[m+1][n-C[m]]
|
|

dp[m][n] --------> dp[m+1][n]

dp[m+1][n-C[m]]が配列外参照になるかの判定が必要です。

1
2
3
4
5
6
7
8
9
10
11
12
N, M = map(int, input().split())
C = list(map(int,input().split()))
INF = 10**10
dp = [[INF]*(N+1) for _ in range(M+1)]
dp[0][0] = 0
for m in range(M):
for n in range(N+1):
if n-C[m]>=0:
dp[m+1][n] = min(dp[m+1][n-C[m]]+1, dp[m][n])
else:
dp[m+1][n] = dp[m][n]
print (dp[M][N])

記事情報

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

DPL1C ナップザック問題

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

動的計画法

はじめに

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

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

問題

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

この問題は動的計画法を用いて解くことができます。

方針

dp[n][w]を考えます。このリストは重さの総和がw以下となるようにn番目までの品物を選んだ時の価値の最大値を表します。

コード

DPL1B 0-1ナップザック問題とほとんど同じコードになります。

貰うDP

1
2
3
4
5
                  dp[n+1][w-wn]
|
|

dp[n][w] --------> dp[n+1][w]

dp[n+1][w-wn]が配列外参照になるかの判定が必要です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
N, W = map(int, input().split())
vw = []
for n in range(N):
v, w = map(int,input().split())
vw.append((v,w))
dp = [[0]*(W+1) for _ in range(N+1)]
for n in range(N):
vn ,wn = vw[n]
for w in range(W+1):
if w-wn >= 0:
dp[n+1][w] = max(dp[n+1][w-wn] + vn, dp[n][w])
else:
dp[n+1][w] = dp[n][w]
print (dp[N][W])

配るDP

1
2
3
4
5
dp[n][w] ----->dp[n+1][w]
|
|

dp[n+1][w+wn]

DP配列を大きめに確保することで、配列外参照を気にする必要がありません。

1
2
3
4
5
6
7
8
9
10
11
12
13
Wmax = 1000
N, W = map(int, input().split())
vw = []
for n in range(N):
v, w = map(int,input().split())
vw.append((v,w))
dp = [[0]*(W+Wmax+1) for _ in range(N+1)]
for n in range(N):
vn ,wn = vw[n]
for w in range(W+1):
dp[n+1][w] = max(dp[n+1][w],dp[n][w])
dp[n+1][w+wn] = dp[n+1][w] + vn
print (dp[N][W])

DPL1B 0-1ナップザック問題と違いwを逆にループさせることはできません。

記事情報

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

DPL1B 0-1ナップザック問題

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

動的計画法

はじめに

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

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

問題

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

この問題は動的計画法を用いて解くことができます。

方針

dp[n][w]を考えます。このリストは重さの総和がw以下となるようにn番目までの品物を選んだ時の価値の最大値を表します。

コード

貰うDP

1
2
3
4
5
dp[n][w-wn] --┐
|
: :
|
dp[n][w] -----┴--> dp[n+1][w]

dp[n][w-wn]が配列外参照になるかの判定が必要です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
N, W = map(int, input().split())
vw = []
for n in range(N):
v, w = map(int,input().split())
vw.append((v,w))
dp = [[0]*(W+1) for _ in range(N+1)]
for n in range(N):
vn ,wn = vw[n]
for w in range(W+1):
if w-wn >= 0:
dp[n+1][w] = max(dp[n][w-wn] + vn, dp[n][w])
else:
dp[n+1][w] = dp[n][w]
print (dp[N][W])

配るDP

1
2
3
4
5
dp[n][w] ----->dp[n+1][w]
|
: :
|
└-->dp[n+1][w+wn]

DP配列を大きめに確保することで、配列外参照を気にする必要がありません。

1
2
3
4
5
6
7
8
9
10
11
12
13
Wmax = 1000
N, W = map(int, input().split())
vw = []
for n in range(N):
v, w = map(int,input().split())
vw.append((v,w))
dp = [[0]*(W+Wmax+1) for _ in range(N+1)]
for n in range(N):
vn ,wn = vw[n]
for w in range(W+1):
dp[n+1][w] = max(dp[n+1][w],dp[n][w])
dp[n+1][w+wn] = dp[n][w] + vn
print (dp[N][W])

wを逆に回しても良いです。

1
2
3
4
5
6
7
8
9
10
11
12
13
Wmax = 1000
N, W = map(int, input().split())
vw = []
for n in range(N):
v, w = map(int,input().split())
vw.append((v,w))
dp = [[0]*(W+Wmax+1) for _ in range(N+1)]
for n in range(N):
vn ,wn = vw[n]
for w in range(W+1)[::-1]:
dp[n+1][w] = dp[n][w]
dp[n+1][w+wn] = max(dp[n][w] + vn,dp[n+1][w+wn])
print (dp[N][W])

記事情報

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

ABC128C Switches

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

全探索

はじめに

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

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

問題

https://atcoder.jp/contests/abc128/tasks/abc128_c

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

方針

Nの最大値は10なので、計算量がO(kM2^N)であるビット全探索でも十分間に合います。

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from itertools import product
N, M = map(int,input().split())
S = []
for m in range(M):
s = list(map(int,input().split()))
S.append(s[1:])
P = list(map(int,input().split()))

ans = 0
for c in product([0,1],repeat=N):
ok = True
for m in range(M):
sm = 0
for s in S[m]:
sm += c[s-1]
if sm % 2 != P[m]:
ok = False
break
if ok:
ans += 1
print (ans)

記事情報

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

ALDS1 5A 総当たり

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

全探索

はじめに

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

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

問題

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

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

方針

nの最大値は20なので、計算量がO(2^n)であるビット全探索でも十分間に合います。

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from itertools import product
n = int(input())
A = list(map(int, input().split()))
q = int(input())
M = list(map(int, input().split()))

create = [0] * 40001

for p in product([0,1], repeat=n):
sm = 0
for i in range(n):
if p[i]==1:
sm += A[i]
create[sm] = 1

for m in M:
if create[m]:
print('yes')
else:
print ('no')

記事情報

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

ALDS1 10A フィボナッチ数列

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

動的計画法

はじめに

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

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

問題

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

この問題は動的計画法を用いて解くことができます。

コード

動的計画法と言っても構える必要はありません。漸化式もシンプルでとても基本的な動的計画法と言えるでしょう。

クエリに関係なく最大サイズのフィボナッチ数列を用意しておき、クエリに応じて数列を参照する実装としました。

1
2
3
4
5
6
7
N = 45
dp = [0] * N
dp[0] = dp[1] = 1
for i in range(N-2):
dp[i+2] = dp[i] + dp[i+1]
n = int(input())
print (dp[n])

記事情報

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

ABC095C Half and Half

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

全探索

はじめに

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

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

問題

https://atcoder.jp/contests/abc095/tasks/arc096_a

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

コード

ABピザを買う枚数を決めると、AピザとBピザを買う枚数を決めることができます。よってABピザを買う枚数について全探索を行えば良いです。計算量はO(N)です。

1
2
3
4
5
A, B, C, X, Y = map(int, input().split())
ans = 10**10
for i in range(10**5+1):
ans = min(ans, A*max(X-i, 0) + B*max(Y-i, 0) + 2*C*i)
print(ans)

もちろん、O(1)で解くことができます。

1
2
3
4
5
6
7
A,B,C,X,Y = map(int,input().split())
min_xy = min(X,Y)
max_xy = max(X,Y)
ans1 = 2 * C * min_xy + A * (X-min_xy) + B * (Y-min_xy) # 無駄にならない範囲でABピザを買い、残りを買う
ans2 = A*X + B*Y # ABピザを買わない
ans3 = 2 * C * max_xy # ABピザだけを買う
print (min(ans1,ans2,ans3))

記事情報

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

ABC167

はじめに

この記事では、AtCoder Beginer Contest 167のA~E問題を解説します。

A - Registration

https://atcoder.jp/contests/abc167/tasks/abc167_a

Pythonの場合、文字列のスライスが便利です。

1
2
3
4
5
6
S = input()
T = input()
if S == T[:-1]:
print ('Yes')
else:
print('No')

B - Easy Linear Programming

https://atcoder.jp/contests/abc167/tasks/abc167_b

minを使って、Kを減らしながらカードを取っていきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
A,B,C,K = map(int,input().split())

ans = 0
mn = min(A,K)
K -= mn
ans += mn

mn = min(B,K)
K -= mn

mn = min(C,K)
K -= mn
ans -= mn
print(ans)

C - Skill Up

https://atcoder.jp/contests/abc167/tasks/abc167_c

bit全探索を行います。itertools.productが便利です。

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
from itertools import product
N,M,X = map(int,input().split())
C = []
A = []
for n in range(N):
a = list(map(int,input().split()))
C.append(a[0])
A.append(a[1:])
INF = 10**9
ans = INF
for p in product([0,1], repeat=N):
sm = [0] * M
cost = 0
for i in range(N):
if p[i]: # bitが立っていたら
for m in range(M):
sm[m] += A[i][m]

cost += C[i]
#print (p,sm)
if min(sm) >= X:
ans = min(ans,cost)
if ans==INF: # 更新されていなければ-1を出力
print (-1)
else:
print (ans)

D - Teleporter

https://atcoder.jp/contests/abc167/tasks/abc167_d

ループを検知すれば良いです。その際に、いつループに入ったかを後ほど利用します。(bias)

具体的には一旦Kからbiasを引いて、ループの省略をした後に再度biasを足します。

これは、単純に周期で割ってしまうとループに入る前の遷移を無視することになってしまうためです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
N,K = map(int,input().split())
A = [0] + list(map(int,input().split()))
turn = [-1] * (N+1) # いつ訪問したかを記録する
c = 1
for k in range(K):
if turn[c]!=-1:
T = k - turn[c] # 周期
bias = turn[c] # バイアス
break
turn[c] = k
c = A[c]
else:
print (c)
exit()

K -= bias
K %= T
K += bias
c = 1
for k in range(K):# 小さくしたKでシミュレーションし直す
c = A[c]
print (c)

E - Colorful Blocks

https://atcoder.jp/contests/abc167/tasks/abc167_e

K=kとしてKを固定して考えて、全てのKについて足し合わせれば良いです。

ペアは全部でN-1個あるので、ペアの選び方は

1
nCk(N-1,k)

となります。
どのようにペアを選んでも、連続する同色ブロックをグループとみなすと、グループは全部でN-kとなります。

グループの列に色を塗っていきます。最初のグループはM色の何でもよく、それ以降のグループは前のグループとは違うM-1色から選ぶことになります。すなわち、グループの列に対する色の塗り方は

1
M * (M-1)^(N-1-k)

となります。

つまり、nCk(N-1,k) * M * (M-1)^(N-1-k)を求めれば良いですが、いずれも巨大になるため工夫してMODの計算をする必要が有ります。

前者はMODの逆元、後者はPythonpow(a,b,MOD)により計算できます。

解答例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
N,M,K = map(int,input().split())
MOD = 998244353
ans = 0

fact = [1] * (N+1) # 階乗を格納するリスト
factinv = [1] * (N+1) # 階乗を格納するリスト
for i in range(N):
fact[i+1] = fact[i] * (i+1) % MOD # 階乗を計算
factinv[i+1] = pow(fact[i+1], MOD-2, MOD)# MODを法とした逆元(フェルマーの小定理)

def nCk(n,k): # 組み合わせ(MOD)を返却する
return fact[n] * factinv[n-k] * factinv[k] % MOD

def solve(k):
r = M * pow(M-1,N-1-k,MOD) * nCk(N-1,k)
return r

for k in range(K+1):
ans += solve(k)
ans %= MOD
print (ans)

失敗例

動的計画法による以下の実装はO(NK)となり間に合いませんが、参考として載せておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
N,M,K = map(int,input().split())
dp = [[0]*(K+2) for _ in range(2)] # 2行で十分
dp[0][0] = M
MOD = 998244353
for n in range(N-1):
cur = n % 2
next = (n+1) % 2
dp[next] = [0]*(K+2)
for k in range(K+1):
dp[next][k] += dp[cur][k] * (M-1)
dp[next][k+1] += dp[cur][k]
dp[next][k] %= MOD
dp[next][k+1] %= MOD
print(sum(dp[(N+1)%2][:-1]))

関連記事

過去のABC解説記事です。

  • ABC166
    • A-F問題を解いています。
  • ABC165
    • A-F問題を解いています。
  • ABC164
    • A-E問題を解いています。
  • ABC163
    • A-D問題を解いています。
  • ABC162
    • A-E問題を解いています。

記事情報

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

ABC032C 列

はじめに

カテゴリーAtCoder版蟻本中級編では、AtCoder 版!蟻本 (中級編)でまとめられている問題をPythonで解いています。

問題

https://atcoder.jp/contests/abc032/tasks/abc032_c

方針

  • しゃくとり法を用います
  • leftを移動する際に、A[left]で割る必要があるためゼロ除算に注意が必要です
  • この問題ではA0が含まれる場合、答えが明らかにNなので、最初に条件分岐してしまえば良いです。

コード

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
N, K = map(int, input().split())
A = []
for n in range(N):
a = int(input())
A.append(a)

if 0 in A:
print(N)
exit()

right = 0
sm = 1
ans = 0
for left in range(N):

while(right<N and sm*A[right]<=K): # rightをインクリメントでs切るか確認
sm *= A[right]
right += 1

ans = max(ans,right-left) # leftに対する条件を満たすパターン数
if (left == right): # leftがrightを超えないようにする
right += 1
else:
sm /= A[left] # leftの移動準備

print (ans)

記事情報

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