ふらふら Diary (仮)

興味のあることを適当に書いていく感じです

G検定2020#2を受けて

2020年7月4日(土)にG検定を受験しました。合格しました。

勉強時間は10時間弱。勉強しなくても知っていたことが多かったし、知らなくても試験中に調べることができるからという理由でほとんど勉強はしなかった。
それでも、一応まとめておこうと思う。

使用した参考書

推薦図書とされる1冊。合格した人がやっておくといいとよく言っている本。
個人的には上記2冊ぐらいは読んでおいた方が良いのでは思う。受験する1年ほど前に読んでいた本。普通に興味があって読んだ。試験対策で読んだわけではなかったけど、3度のブームと冬の時代を繰り返したこととか上記2冊で出題された内容が書かれていて、読んでいてよかったと思った。実際、この本を読んでいたから試験対策をしなくてもよかったぐらい参考になった。

あとは、機械学習の基本的なところを理解していればいいかなと思う。

私の場合は問題集にある総仕上げ問題を何も見ずに解いて8割ほど合っていた(これで2時間ほど使った)ので、残り(8時間ほど)は電車の中で公式テキストと問題集を読んでいた感じだった。
正直いうと公式テキストを完璧に覚えても試験に合格するかは分からない。むしろ公式テキストに載っていない問題が多すぎて"過学習"しているのではと思ってしまうほど。試験に合格するためのだけの勉強をしても意味がないということをG検定を通して教えてくれているのかもしれない。
あと法律の問題がたくさん出題された。これも公式テキストには載っていなくて驚いた。文脈で分かってしまうものもあればよく分からなくて飛ばしてしまう問題もあった。逆に、公式テキストや問題集には人名がたくさん載っていたけど、本番では一人も出題されなくて残念だった。

おわりに

あくまでもG検定なので技術的なところはそんなに詳しく知らなくてもいいし、AIの歴史とかが出題されるので、試験をして何をみたいのだろうという感じ。
E資格は受験するまでが大変そうなので今のところ受験するつもりはない。
G検定を取得したところで特に何かすごいということもないと思うけど、取りました!とは言えるので興味があれば勉強するのもありかな。幸い、現時点では不合格者よりも合格者の方が多くて合格する確率も高いので。
まあでも公式テキストを完璧にすれば合格できるような試験にはして欲しいなと思う。

irisでロジスティック回帰

はじめに

ロジスティック回帰は、教師あり学習における分類を行うアルゴリズムである。scikit-learnライブラリでロジスティック回帰をする方法についてirisデータセットを使ってまとめてみた。

二値分類

ロジスティック回帰は、二値分類のアルゴリズムである。教師あり学習には以下の二つがある。

  • 回帰: 数値を予測する
  • 分類: カテゴリを予測する
アルゴリズム

基本的な考え方は線形回帰と同様で、データ \mathbf{x}に対して重みベクトル \mathbf{w}を掛けてバイアス w_0を加えた \mathbf{w}^T\mathbf{x}+w_0を計算する。ロジスティック回帰では確率を計算するため出力の範囲を0以上1以下に制限する必要がある。そのため、シグモイド関数を用いることで0から1の間の数値を返している。シグモイド関数は以下のようになる。
 \displaystyle \sigma (z) = \frac{1}{1+e^{-z}}
シグモイド関数を用いることで0から1の間の数値を返している。シグモイド関数をグラフに書くと次のような形状になる。

import math

# シグモイド関数
def sigmoid(x):
    y = 1 / (1 + math.e**(-x))
    return y

プロットしてみる。

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-10, 10, 100)
y = sigmoid(x)
plt.plot(x, y)
plt.show()

f:id:jetarinA:20200802102455p:plain
このシグモイド関数を用いて、データ  \mathbf{x} が与えられたときに、そのラベルが  y である確率  p p = \sigma (\mathbf{w}^T \mathbf{x} + w_0) で計算する。二値分類であれば  pが0.5未満のとき0、0.5以上のとき1というように分類を行う。
学習では、ロジスティック損失を誤差関数として用い、これを最小化する。最小化では勾配降下法を用いる。

具体例

irisのデータセットでロジスティック回帰をしてみる。

import pandas as pd
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

# irisデータセットの読み込み
data = load_iris()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.DataFrame(data.target, columns=['Species'])
df = pd.concat([X, y], axis=1)
df.head()
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) Species
0 5.1 3.5 1.4 0.2 0
1 4.9 3.0 1.4 0.2 0
2 4.7 3.2 1.3 0.2 0
0 4.6 3.1 1.5 0.2 0
0 5.0 3.6 1.4 0.2 0

irisのデータセットにはあやめの sepal length(がくの長さ)、sepal width(がくの幅)、petal length(花弁の長さ)、petal width(花弁の幅)に加え、それぞれの品種(setosa、versicolor、virginica)の情報がある。
今回は品種 setosa、versicolor のpatal length、petal widthのデータを使用してロジスティック回帰をしてみる。

# 品種 setosa、versicolorを抽出
df2 = df[(df['Species']==0) | (df['Species']==1)]
# 説明変数
X = df2.iloc[:, [2, 3]]
# 目的変数
y = df2.iloc[:, 4]
# 学習データと検証データを分割
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
model = LogisticRegression()
# 学習
model.fit(X_train, y_train)

切片と傾きを確認する。

# 切片
print(model.intercept_)
[-7.3018626]
# 傾き
print(model.coef_)
[[2.41092643, 1.01214654]]

ここで、決定境界をプロットしてみる。決定境界とは、分類結果が切り替わる境目のことで、ロジスティック回帰の場合、決定境界は確率を計算した結果がちょうど50%になる箇所を指す。
まず、以下の通り定義する。
 x_1: petal length (cm)
 x_2: petal width (cm)
バイアス:  w_0
重みベクトル \mathbf{w} = \left(\begin{array}{c}w_1 \\w_2 \end{array} \right)
データ \mathbf{x} = \left(\begin{array}{c}x_1 \\x_2 \end{array} \right)

データ \mathbf{x} が与えられたとき、そのラベルが  y である確率  p
 \displaystyle \sigma (z) = \frac {1}{1+e^{-z}}であるから
 p = \sigma(\mathbf{w}^T \mathbf{x} + w_0)
確率0.5のとき決定境界が求まるので
 \displaystyle \begin{eqnarray} \frac{1}{2} &=& \sigma (\mathbf{w}^T \mathbf{x} + w_0) \\ 
&=& \frac{1}{1 + e^{-(\mathbf{w}^T \mathbf{x} + w_0)}} \\
e^{-\mathbf{w}^T \mathbf{x} + w_0} &=& 1 \\
\mathbf{w}^T \mathbf{x} + w_0 &=& 0 \\
\left( \begin{array}{cc}w_1 & w_2 \end{array} \right) \left( \begin{array}{c} x_1 \\ x_2 \end{array} \right) + w_0 &=& 0 \\
w_1 x_1 + w_2 x_2 + w_0 &=& 0 \\
x_2 &=& -\frac{1}{w_2} (w_1 x_1 + w_0)
\end{eqnarray}
となり、これが平面の場合の決定境界となる。
Pythonでプロットしてみる。

w_0 = model.intercept_
w_1 = model.coef_[0, 0]
w_2 = model.coef_[0, 1]

x1 = np.linspace(0, 6, 30)
x2 = (-w_1 * x1 - w_0) / w_2
# プロット
plt.plot(x1, x2, color='gray')
plt.scatter(X.iloc[:, 0][y==0], X.iloc[:, 1][y==0], color='lightskyblue', label=data.target_names[0])
plt.scatter(X.iloc[:, 0][y==1], X.iloc[:, 1][y==1], color='sandybrown', label=data.target_names[1])
plt.ylim(-0.25, 2)
plt.xlabel(X.columns[0])
plt.ylabel(X.columns[1])
plt.legend()
plt.show()

f:id:jetarinA:20200807101705p:plain
今回はもともときれいに分かれていたので、決定境界もいい感じに引けていた。

特徴量の解釈について

ロジスティック回帰では各特徴量の係数を見ることができる。目的変数としてversicolorを0、setosaを1として用いているとき、各特徴量は以下のようになる。

petal length (cm) petal width (cm)
2.41092643 1.01214654

先ほどのグラフの軸petal length、petal widthの重みは正の値だったため、どちらも大きくなるとversicolorである割合が大きくなることが分かる。
各特徴量の係数の符号を見ることで、正の影響を与えているのか負の影響を与えているのかが解釈できる。

おわりに

scikit-learnライブラリでロジスティック回帰をする方法についてirisデータセットを使って記述した。ロジスティック損失なども記述していけたらと思う。

多項式回帰に正則化を利用

はじめに

正則化とは過学習を防ぐための手法の一つであり、モデルの汎化性能の向上に役立つ。過学習は学習データに対してうまく予測できているが、未知のデータ(検証データ)に対しては、うまく汎化できないことを意味する。

過学習していく様子

まずは過学習のイメージを掴んでおく。今回は、前回の記事で示したモデルに、正規分布に従う乱数を加えて生成したデータを使用する。

# 前回の復習
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.linear_model import LinearRegression

data = load_iris()
df = pd.DataFrame(data.data, columns=data.feature_names)
X = df.iloc[:, 0][data.target==0].values[:, np.newaxis]
y = df.iloc[:, 1][data.target==0].values
model = LinearRegression()
model.fit(X, y)

データの生成から行う。

# 上記のモデルに乱数を加える
# 学習データ
X_train = 10 * np.random.rand(20, 1)
y_train = model.predict(X_train) + np.random.randn(20)
# 検証データ
X_test = 10 * np.random.rand(12, 1)
y_test = model.predict(X_test) + np.random.randn(12)

プロットしてみる。

import matplotlib.pyplot as plt
plt.scatter(X_train, y_train, color='black', label='train')
plt.scatter(X_test, y_test, color='gray', label='test')
plt.legend()
plt.show()

f:id:jetarinA:20200515165147p:plain
このデータを多項式回帰でモデル化することを考える。線形回帰を1次式、2次式…と次数を増やしていくにつれてどのように変化していくかを調べる。同時に平均2乗誤差も見てみる(これを最小化するように学習しているため)。

from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import mean_squared_error

y_quad_fit, train_error, test_error = [], [], []
# 次数を 1 から 6 まで増やす
for n in range(6):
    quadratic = PolynomialFeatures(degree=n+1)
    pr = LinearRegression()
    X_quad = quadratic.fit_transform(X_train)
    pr.fit(X_quad, y_train)
    X_fit = np.linspace(X_train.min(), X_train.max(), 10)[:, np.newaxis]
    y_quad_fit.append(pr.predict(quadratic.fit_transform(X_fit)))
    # 学習誤差
    train_error.append(mean_squared_error(pr.predict(X_quad), y_train))
    # 検証誤差
    test_error.append(mean_squared_error(pr.predict(quadratic.fit_transform(X_test)), y_test))

各次数 d での学習結果を可視化したものは以下に示す。

plt.scatter(X, y)
plt.plot(X_fit, y_quad_fit[0], color='firebrick', label='d=1')
plt.plot(X_fit, y_quad_fit[1], color='darkorange', label='d=2')
plt.plot(X_fit, y_quad_fit[2], color='gold', label='d=3')
plt.plot(X_fit, y_quad_fit[3], color='mediumseagreen', label='d=4')
plt.plot(X_fit, y_quad_fit[4], color='skyblue', label='d=5')
plt.plot(X_fit, y_quad_fit[5], color='mediumpurple', label='d=6')
plt.legend()
plt.show()

f:id:jetarinA:20200515165159p:plain
また、次数ごとの平均2乗誤差は以下の通り。学習誤差は学習データにおける平均2乗誤差、検証誤差は検証データにおける平均2乗誤差を表している。

次数 学習誤差 検証誤差
1 1.270780 0.931902
2 1.164353 0.433551
3 1.159767 0.503716
4 0.786159 7.871051
5 0.779189 11.882549
6 0.679223 92.420817

次数を大きくすると複雑な曲線になり、学習誤差がだんだんと小さくなる。学習誤差はd=6のときに最も小さくなるが、検証誤差は学習誤差に比べかなり大きくなっている。このモデルは、過学習しているため汎化性能の低いモデルとなっている。

正則化を利用

次に、多項式回帰に正則化を利用した結果を示す。今回はRidge回帰を使用した。

from sklearn.linear_model import Ridge

y_ridge_fit, train_ridge_error, test_ridge_error = [], [], []
# 次数を 1 から 6 まで増やす
for n in range(6):
    quadratic = PolynomialFeatures(degree=n+1)
    X_ridge = quadratic.fit_transform(X_train)
    ridge = Ridge(alpha=0.5)
    ridge.fit(X_ridge, y_train)
    X_fit = np.linspace(X_train.min(), X_train.max(), 10)[:, np.newaxis]
    y_ridge_fit.append(ridge.predict(quadratic.fit_transform(X_fit)))
    # 学習誤差
    train_ridge_error.append(mean_squared_error(ridge.predict(X_ridge), y_train))
    # 検証誤差
    test_ridge_error.append(mean_squared_error(ridge.predict(quadratic.fit_transform(X_test)), y_test))

各次数 d での学習結果を可視化したものは以下に示す。

plt.scatter(X_train, y_train, color='black', label='train')
plt.scatter(X_test, y_test, color='gray', label='test')
plt.plot(X_fit, y_ridge_fit[0], color='firebrick', label='d=1')
plt.plot(X_fit, y_ridge_fit[1], color='darkorange', label='d=2')
plt.plot(X_fit, y_ridge_fit[2], color='gold', label='d=3')
plt.plot(X_fit, y_ridge_fit[3], color='mediumseagreen', label='d=4')
plt.plot(X_fit, y_ridge_fit[4], color='skyblue', label='d=5')
plt.plot(X_fit, y_ridge_fit[5], color='mediumpurple', label='d=6')
plt.legend()
plt.show()

f:id:jetarinA:20200515165206p:plain
また、次数ごとの平均2乗誤差は以下の通り。

次数 学習誤差 検証誤差
1 1.270831 0.944950
2 1.164395 0.433568
3 1.160380 0.478892
4 0.848420 4.590636
5 0.793668 16.438639
6 0.784268 29.653928

正則化によりモデルの複雑さが抑えられ、誤差も次数が増えたときの検証誤差が抑えられており、過学習を防ぐことができていることが確認できる。
そうはいっても今回はデータが良くなかった可能性もあるが、全体的にあまりいい結果は出なかった。

アルゴリズム

簡単のため、2次式の線形回帰に正則化を利用した場合を考える。
 \displaystyle R(w) = \sum^n_{i=1} \{y_i-(w_0+w_1x_i+w_2x_i^2)\}^2+\alpha(w_1^2+w_2^2)
右辺の第1項は線形回帰の損失関数、第2項は罰則項と呼ばれるもので、学習パラメータの2乗和の形をとる。また、 \alpha (\geq0) は、正則化の強さをコントロールするパラメータである。
ここで、Ridge回帰の損失関数  R(w) を最小化することを考える。右辺第2項は学習パラメータの2乗和になっているため、学習パラメータの絶対値が大きいと、損失関数全体が大きくなる。 \alpha により学習パラメータを抑えることが可能となっている。
適切な  \alpha は検証誤差を見ながら調整するのが一般的である。

Ridge回帰とLasso回帰

Ridge回帰は、誤差関数の罰則項が学習パラメータの2乗和であるが、この罰則項を別の形にすることで、異なる性質の正則化を行うことができる。代表的な正則化手法として罰則項が学習パラメータの絶対値の和となっているLasso回帰がある。Lasso回帰の誤差関数は次のようになる。
 \displaystyle R(w) = \sum^n_{i=1}\{y_i-(w_0+w_1x_i+w_2x_i^2)\}+\alpha(|w_1|+|w_2|)
Lasso回帰は学習パラメータが0になりやすいという性質がある。この性質により特徴量選択を行うこともでき、モデルを解釈しやすくなるという利点もある。

おわりに

scikit-learnライブラリで多項式回帰に正則化を利用する方法についてまとめた。正則化を利用することでモデルの複雑さを緩和し、モデルの汎化性能の向上に役立つことを示した。正則化手法については、代表的なものとしてRidge回帰及びLasso回帰について簡単にまとめたが、今後は学習パラメータが計算されるときのイメージなどをまとめていきたい。

irisで線形回帰

はじめに

線形回帰は予測を行うアルゴリズムである。scikit-learnライブラリで線形回帰をする方法についてirisデータセットを使ってまとめてみた。

線形回帰

「ある説明変数が大きくなるにつれて、目的変数も大きく(または小さく)なっていく」関係性をモデル化する手法。
以下の表のように、x が大きくなると y x の定数倍大きくなる状態をいう。

  • 説明変数:  x
  • 目的変数:  y
 x  y
1 2
2 4
3 6
4 8
アルゴリズム

 y = w_0 + w_1 x で表す。この  w_0 w_1 を求めることで、 x が決まれば  y が決まる状態になる。
平均二乗誤差
適切な  w_0 w_1 を求めるために平均二乗誤差を利用することで定量的に判断をすることができる。
平均二乗誤差とは、 n 個のデータが存在するとき、以下の式で表す。
 \dfrac{\Sigma^{n}_{i=1}\{y_i - (w_0 + w_1x_i) \}^2 }{n}
目的変数  y_i と直線  w_0 + w_1x_i の差を 2 乗し、その平均を計算したものとなる。
線形回帰はさまざまな直線のうち、平均二乗誤差が最小となる  w_0 w_1を求める。
分からなかった点としては、最小二乗法だと  \Sigma^n_{i=1}\{y_i - (w_0 + w_1x_i)\}^2 から求めるけど、なぜ平均二乗誤差の最小化と記述されているのかというところ。

具体例

irisのデータセットで線形回帰をしてみる。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.linear_model import LinearRegression

# irisデータセットの読み込み
data = load_iris()
df = pd.DataFrame(data.data, columns=data.feature_names)
df.head()
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2

irisのデータセットにはあやめの sepal length(がくの長さ)、sepal width(がくの幅)、petal length(花弁の長さ)、petal width(花弁の幅)に加え、それぞれの品種(setosa、versicolor、virginica)の情報がある。
今回は品種 setosa のsepal length、sepal widthのデータを使用して線形回帰をしてみる。

# setosa の sepal length と sepal width の散布図をプロット
plt.scatter(df.iloc[:, 0][data.target == 0], df.iloc[:, 1][data.target == 0])
plt.xlabel(df.columns[0])
plt.ylabel(df.columns[1])
plt.show()

f:id:jetarinA:20200506143603p:plain
setosaのsepal lengthとsepal widthの散布図
何となく線形になっていることが分かる。

model = LinearRegression()

# 説明変数
X = df.iloc[:, 0][data.target==0].values.reshape(-1, 1)
# 目的変数
y = df.iloc[:, 1][data.target==0].values

# 学習
model.fit(X, y)
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)

切片と傾きを確認する。

# 切片
print(model.intercept_)
-0.5694326730396493
# 傾き
print(model.coef_)
[0.7985283]

回帰直線を引き確認する。

plt.scatter(X, y)
plt.plot(X, model.predict(X), color='red')
plt.xlabel(df.columns[0])
plt.ylabel(df.columns[1])
plt.show()

f:id:jetarinA:20200506145624p:plain
結果
ある程度当てはまっていること思われる。

おわりに

scikit-learnライブラリで線形回帰をする方法についてirisデータセットを使って記述した。決定係数や線形回帰がうまく当てはまらない例、重回帰については今後まとめていきたい。

Pythonでカレンダー表示

カレンダーを表示

>>> import calendar
>>> my_calendar = calendar.TextCalendar(firstweekday=6)
>>> my_calendar_string = my_calendar.formatmonth(2018, 11)
>>> print(my_calendar_string)

   November 2018
Su Mo Tu We Th Fr Sa
             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

これで普通にカレンダー表示できたけど、GUIツールでは表示できなかった。
なので別の方法を検討。調べていくうちに、日をリストにすればいいのではと思った。

>>> my_calendar.monthdayscalendar(2018, 11)

[[0, 0, 0, 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, 0]]

日付が入らないところは0になるみたい。
日にちを一つずつ扱っていけばいい感じに表示できそう。

PythonでGUIツール作成するために考えていること

Pythonは何でもできる、ということで何か作りたいなと思い、作っていこうと思う。最終的なアウトプットが具体的に決まっているわけではないが、ひとまずやりたいことを少しずつやっていこうと思う。

やりたいこと

一番はデータの可視化。そのために日付とそれに関連するデータかと。自分で手に入れられるというとやはりお金に絡むことで家計簿みたいな感じかな。あとは日付に関連するというと天気や気温の取得など。他には、スケジュールとか睡眠時間とか読書の記録とか…なんとなく考えているのはあるけど、明確には決めていない。
そしてGUIツールを作りたい、ということで…。初心者すぎるため、ひとまずTkinterでやってみようと思う。

/* 基本情報技術者試験を受けて */

平成29年10月15日(日)に基本情報技術者試験を受けました。合格しました。

 

試験勉強はだいたい1日2時間×3か月程度だと思う。ちなみにこの勉強時間は全然あてにならなくて、後述するが、参考書を買ったのは約1年ぐらい前だったし、私はよく言う事前知識なしの文系でもない。約1年ほど前から勉強を開始していたけど、短期集中していなかったと言うのが正しいと思われる。短期集中できなかったことが第一の失敗。

 

使用した参考書

 合格した人の多くが使用した参考書のひとつ。

キタミ式イラストIT塾 基本情報技術者 平成28年度

キタミ式イラストIT塾 基本情報技術者 平成28年度

 

 平成28年度というところがぽいんと(何がポイントなのか)。最新のを買うのがいいと思います。

 

 これは午後対策で買ったのだけれど、あまり参考にしていない。事前知識で知っているところが多かったので。擬似言語のところを読んだけど、これ問題冊子にちゃんと書いてあって覚えなくてもいいんだね。必死で覚えようとしていた。

 

苦しんで覚えるC言語

苦しんで覚えるC言語

 

 C言語を選択しようということで基本的なところは「苦C」で勉強。

 

改訂新版 基本情報技術者試験 C言語の切り札

改訂新版 基本情報技術者試験 C言語の切り札

 

 過去問の解説が載っているので、時間を測ってやってみた。

 

勉強方法

午前の対策は「キタミ式」を2周読んでひたすら過去問を解く。過去問で間違えたところのうち、「キタミ式」に載っている用語などは確実に覚える。それだけで合格点に達する。ちなみに私は試験1週間前になっても合格点に達していないことがあった。ある程度やれば合格点には達するが、やるだけ満点に近づくものでもなさそう。

午後の対策は半年前からちょーゆるゆるにアルゴリズムの本と「C言語の切り札」を勉強し始めた。午後の試験は全部で6割以上取れば合格なので、アルゴリズムC言語(ソフトウェア開発)で半分ほど取れればよいでしょ、という感じ。すでにやる気がなかった。3か月前から過去問をやり始めた。時間を測らずに解いたところ、すでに合格点に達してしまったので、そこからやる気を失った。午後の試験は時間配分に気を付けないと時間が足りなくなるので、そこの実践はしておいた方がいい。私も1か月前はちゃんと時間を測ってやった。

私はまず、アルゴリズムを30分で解いて、次は1番得意なデータベースを15分弱で解いて、あとは解けそうな選択問題(C言語を除く)、そして最後にC言語30分で解いた。アルゴリズムC言語(ソフトウェア開発)の問題は配点が高く、解くのにも時間がかかるので、その分の時間は必ず確保しておくこと。

おわりに

午後の試験、みんな早く退室してしまうけど、どんな速さで解いているの?私はいつも時間いっぱいかかっていたよ!

試験を受けるのにもお金がかかるので、試験勉強やらずに不合格になるよりはやって不合格の方がいいかなと思う。このままだと午後は合格しても午前は数点足らずに不合格になってしまうと思って、1週間前からやっと本格的に勉強し始めた私が言うのもあれですが。