浅野直樹の学習日記

この画面は、簡易表示です

浅野直樹

python3エンジニア認定基礎試験を受験しました

python3エンジニア認定基礎試験を受験してきました。その記録を残しておきます。

 

1.結果

自信がなかった数問のうち1問を間違えたようです。

 

2.受験動機

見様見真似でpythonを趣味や仕事で使ってきました。わからないことがあればその都度調べていました。基本的な文法などを調べずにすらすらとコードを書けるようになりたくて、python3エンジニア認定基礎試験の受験を決意しました。

 

3.模擬試験

python3エンジニア認定基礎試験を受験しようと少しでも思う人は、模擬試験を受けてみるべきです。インターネット上で簡単に無料で受験できます。

 

DIVER | DIVE INTO CODE(DIC)

 

PRIME STUDY(プライム・スタディ) – Python試験とPHP試験の無料模擬試験サイト

 

問題の形式や雰囲気は本番そっくりです。

 

本番の試験の難易度は、DIVERより難しく、PRIME STUDYより簡単だという印象でした。

 

DIVERのほうはランダムに40問出題されるので知識のインプットと同時並行で普段使いをし、3回分の問題セットがあるPRIME STUDYを節目節目で活用するのがよいと思います。私もそうしました。

 

そしてこの模擬試験で安定して高得点を取れるようになり、なぜそうなるのか、なぜ他の選択肢ではダメなのかを説明できるようになれば、合格が見えてきます。

 

4.本などの教材

優先度が高い順番に紹介します。

 

(1)Pythonチュートリアル

Python チュートリアル — Python 3.10.0b2 ドキュメントが最重要です。Pythonエンジニア認定試験 | Odyssey CBT | オデッセイ コミュニケーションズの出題率と照らし合わせて強弱をつけてもよいです。おそらくこのチュートリアルに書いてある内容しか出題されません。

 

(2)Pythonエンジニア育成推進協会監修 Python 3スキルアップ教科書


Python 3スキルアップ教科書 = Python 3 Skill-up Textbook


作 者: Pythonエンジニア育成推進協会 監修,辻真吾, 小林秀幸, 鈴木庸氏, 細川康博 著

出版社: 技術評論社

発売日: 2019年11月06日

チュートリアルは無味乾燥で記述が簡潔すぎると感じたらこの本がよいでしょう。python3エンジニア認定基礎試験に準拠しています。

 

(3)入門 Python 3


入門Python 3


作 者: Bill Lubanovic 著,鈴木駿 監訳,長尾高弘 訳

出版社: オライリー・ジャパン

発売日: 2021年04月19日

python3エンジニア認定基礎試験に準拠してはいませんが、およそ内容はかぶっています。手を動かして練習問題を解くと力がつきます。15章のデータと時間:プロセスと並行処理以降はざっと目を通すだけでよいでしょう。

 

(4)初めてのPython


初めてのPython


作 者: Mark Lutz 著,夏目大 訳

出版社: オライリー・ジャパン

発売日: 2009年04月03日

主にpython2が基準となっており古さを感じますが、それがかえって新しい便利な機能の理解を深めてくれたりもします。部ごとについている練習問題で手を動かすと理解が増します。

 

5.受験後の感想

検索せずにファイルのオープンやリスト内包表記ができるようになったので、pythonのコードを書くときにひっかかることが減りました。python3エンジニア認定基礎試験はpythonを学ぶ一つのきっかけや視点になると思います。



令和3年司法試験予備試験成績通知(論文)

令和3年司法試験予備試験論文の成績通知を公開します。過去の結果は以下のリンクにあります。

試験科目 順位ランク
憲法 E
行政法 C
民法 E
商法 B
民事訴訟法 C
刑法 E
刑事訴訟法 B
一般教養科目 E
法律実務基礎科目 C
合計点 224.07
順位 772

再現答案も過去の記事にありますので、ご参考になれば幸いです。

ほんの少しではありますが、手応えと結果が一致しつつあるのはいい傾向だと思います。



令和3年司法試験予備試験成績通知(短答)

令和3年司法試験予備試験短答の成績通知を公開します。過去の結果は以下のリンクです。

試験科目 得点
憲法 22
行政法 14
民法 24
商法 22
民事訴訟法 17
刑法 22
刑事訴訟法 19
一般教養科目 54
合計点 194
順位 327

短答式の結果は安定してきたかもしれません。



ランダムに取得したWikipediaの記事テキストの類似度を求める

WikipediaのAPIを使ってランダムに記事本文を取得する – 浅野直樹の学習日記JanomeとMeCabで日本語のテキストを単語に分ける – 浅野直樹の学習日記で下準備ができました。

これでいよいよランダムに取得したWikipediaの記事テキストの類似度を求めるというやりたかったことができます。

 

1.WikipediaのAPIを使って本文が長い記事をランダムに取得する

テキストの類似度を求めてそれっぽい結果を得るためには記事本文が長いほうがよいです。

そこで、ランダムに100件の記事を取得し、記事本文が長いほうから上位20件だけを抽出します。

まずはランダムに100件の記事のリストを取得し、それぞれの長さも取得して、長い順に並べます。

import pandas as pd
import requests

#結果を格納するデータフレームを作成し、表示行数を設定
df = pd.DataFrame(columns=["page_id", "title", "linked_title", "length"])
pd.set_option('display.max_rows', 6)

#wikipediaに接続するための基本設定
S = requests.Session()
URL = "https://ja.wikipedia.org/w/api.php"

#ランダム記事リストから記事本体を取得するためのパラメータの設定
GENERATOR_PARAMS = {
    #共通パラメータ
    "action": "query",
    "format": "json",
    #generatorパラメータ
    "generator": "random",
    "grnlimit": "100",
    "grnnamespace": "0",
    #記事情報パラメータ
    "prop": "info",
    "inprop": "url",
}

#ランダム100件の記事情報の取得
GENERATOR_R = S.get(url=URL, params=GENERATOR_PARAMS)
GENERATOR_DATA = GENERATOR_R.json()
for k, v in GENERATOR_DATA["query"]["pages"].items():
    page_id = k
    (title, fullurl, length) = (v["title"], v["fullurl"], v["length"])
    linked_title = "<a href='{}'>{}</a>".format(fullurl, title)    
    df.loc[len(df)+1] = [page_id, title, linked_title, length]

#長さ(length)の長い順に並べ替えて表示
sorted_df = df.sort_values("length", ascending=False)
ranked_df = sorted_df.set_axis(range(1, (len(df)+1) ), axis="index")
print(ranked_df[["page_id", "title", "length"]])

これで簡易的に上位3件と下位3件が表示されるはずです。

せっかくなのできれいに表示してみましょう。

from IPython.display import HTML

longest_3_df = ranked_df.query('index <= 3')

#ブログ記事執筆用にhtmlタグを出力
print(longest_3_df[["page_id", "linked_title", "length"]].to_html(escape=False))

#jupyter notebook内での確認用に整形された表を出力
HTML(longest_3_df[["page_id", "linked_title", "length"]].to_html(escape=False))
page_id linked_title length
1 97822 ジェンソン・バトン 118405
2 1296390 A.J.フォイト 45067
3 4249835 ブカレスト市電 43454
shortest_3_df = ranked_df.query('index >= 98')

#ブログ記事執筆用にhtmlタグを出力
print(shortest_3_df[["page_id", "linked_title", "length"]].to_html(escape=False))

#jupyter notebook内での確認用に整形された表を出力
HTML(shortest_3_df[["page_id", "linked_title", "length"]].to_html(escape=False))
page_id linked_title length
98 2933094 パルラディ 591
99 833807 Uボート (曖昧さ回避) 341
100 857624 赤えい 224

例えばこのように表示されます。

毎回結果は異なります。

それでは上位20件の記事本文を取得しましょう。

import time

#記事本体を取得するためのパラメータの設定
ARTICLE_PARAMS = {
    "action": "query",
    "format": "json",
    "prop": "extracts",
    "explaintext": True,
    "exsectionformat": "plain",
}

#長さの上位20件を抽出
longest_20_df = ranked_df.query('index <= 20')

#記事本文を一時的に保存するリストの作成
contents = []

for page_id in longest_20_df["page_id"]:
    #ページIDから記事情報の取得
    ARTICLE_PARAMS["pageids"] = str(page_id)
    ARTICLE_R = S.get(url=URL, params=ARTICLE_PARAMS)
    ARTICLE_DATA = ARTICLE_R.json()

    #記事本文をリストに追加
    content = ARTICLE_DATA["query"]["pages"][str(page_id)]["extract"]
    contents.append(content)

    #高速でリクエストを繰り返すことで負担をかけないように1秒待つ
    time.sleep(1)

#データフレームに記事本文の列を追加
contents_df = longest_20_df.assign(content=contents)
print(contents_df)

これで必要なデータを用意することができました。

 

2.テキストのベクトル化

機械学習をするためには、サンプル数×特徴量数の2次元配列(ベクトル)を作ります。

テキストをベクトル化する際の考え方や実際のコードはPythonで文章の類似度を計算する方法〜TF-IDFとcos類似度〜 | データサイエンス情報局がわかりやすかったです。

今回のサンプル数は20です。特徴量数は全記事に含まれる単語の種類の数になります。

基本的にsklearn.feature_extraction.text.TfidfVectorizer — scikit-learn 0.24.2 documentationを使えばよいのですが、これを日本語に適用するためにはそれなりの準備が求められます。

投入するデータはサンプルごとの文字列のシーケンス(リストなど)なのでもう用意できているとして、日本語では単語に分割(分かち書き)するためのanalyzerが必須となります。ここでjanomeかmecabを使います。

また、あまりにありふれすぎていてテキストの特徴を判別する手がかりにならないストップワードも設定したほうがよいです。【自然言語処理入門】日本語ストップワードの考察【品詞別】 – ミエルカAI は、自然言語処理技術を中心とした、RPA開発・サイト改善・流入改善レコメンドエンジンを開発からそのまま使わせていただきました。

英語であればTfidfVectorizerのパラメータにストップワードを設定できるのですが、日本語ではそれができないため、単語に分割(分かち書き)するときにフィルターにかけます。

import MeCab
from sklearn.feature_extraction.text import TfidfVectorizer

#ストップワードの設定
stop_words_str = "。 は 、 の ( ) に で を た し が と て ある れ さ する いる から も ・ として 「 」 い こと – な なっ や れる など ため この まで また あっ ない あり なる その られ 後 『 』 へ 日本 という よう ( 現在 もの より だ おり 的 中 により ) 2 による 第 なり によって 1 これ その後 ず , か 時 なく られる だっ において 者 なかっ 行わ 多く しかし 3 せ 他 名 出身 それ について 間 当時 上 ば 存在 受け . 呼ば 同 なお できる 目 行っ 内 う 数 のみ 前 以下 き : 元 化 4 等 および 使用 でき 同年 主 場合 際 一 約 における さらに 一部 所属 人 以降 ら 活動 5 中心 作品 いう 知ら 同じ 初 だけ 多い 時代 以上 生まれ 発表 2010年 にて 見 務め 持つ とともに 大 参加 頃 位置 2007年 2009年 2008年 開始 うち 行う ほか 特に 全 ながら 当初 発売 せる 2011年 家 かつて 下 卒業 一つ 2006年 6 でも 年 2012年 形 用い に対して 最初 / 本 考え なら 以外 関係 一方 それぞれ 各 同様 4月 経 2013年 と共に 2005年 そして 3月 地域 必要 これら 及び 一般 用 2014年 結果 可能 現 開催 事 ものの 利用 にかけて 部 影響 設立 記録 得 アメリカ 通り とも 彼 2015年 自身 登場 始め または 担当 変更 意 味 たり 側 とき 開発 設置 代表 ほど ので 構成 ただし 二 2004年 郡 初めて たち 部分 2016年 最も 放送 7 旧 地 最後 アメリカ合衆国 10月 世界 研究 大学 8 系 大きな 活躍 獲得 続け 以前 全て 問題 性 与え 9月 父 含む といった ほとんど 7月 ところ 2017年 2003年 向け 持っ 2000年 加え 使わ 型 6月 に関する 出場 12月 目的 高い 名称 に対する 1月 万 実際 5月 -1 名前 様々 再び 10"
stop_words = stop_words_str.split()

#単語に分割してストップワードをフィルタリングする関数の作成
def analyzer(text):
    tagger = MeCab.Tagger("-Owakati")
    wakati = tagger.parse(text)
    filtered_wakati = filter(lambda x: x not in stop_words, wakati.split())
    return filtered_wakati

#テキストのベクトル化
documents = contents_df["content"]
vectorizer = TfidfVectorizer(analyzer=analyzer)
X = vectorizer.fit_transform(documents)
print(X.toarray())

これでテキストのベクトル化ができました。非常に疎(0の割合が多い)なベクトルが出力されます。

サンプル数×特徴量数の2次元配列(ベクトル)になっているか確認してみましょう。

print(X.toarray().shape)
(20, 8302)

よさそうです。

特徴量の内訳も見てみましょう。

print(vectorizer.vocabulary_)
{'ジェンソン': 2723, 'アレクサンダー': 2235, 'ライオンズ': 3690, 'バトン': 3183, 'Jenson': 821, 'Alexander': 484, 'Lyons': 883, 'Button': 589,
【以下略】

うまくいっているようです。

 

3.コサイン類似度を計算する

ここまでできたらあともう少しです。

作成したベクトルのコサイン類似度を求めます。

from sklearn.metrics.pairwise import cosine_similarity

#類似度行列の作成
cs_array = cosine_similarity(X)
print(cs_array)

20×20の行列が出力されます。

値が1に近いほど類似しており、0に近いほど類似していません。

当然ながら、同じテキストを比べている対角成分は1になっています。

この類似度行列から、その記事自体は除外して、最も似ている3つの記事と、最も似ていない3つの記事のタイトルを表示してみます。

cs_arrayはインデックスが0から始まり、contents_dfは記事の長さの順位を表わすために1から始まっていることに注意してください。

import numpy as np

for index, item in enumerate(cs_array):
    #その記事のタイトル
    print("・「{}」".format(titles[index+1]))

    #似ている3つ
    print("<似ている3つの記事>")
    best_3 = sorted(item, reverse=True)[1:4]
    for best in best_3:
        best_index = np.where(item == best)[0][0]
        print("{}({})".format(contents_df["title"][best_index+1], best))

    #似ていない3つ
    print("<似ていない3つの記事>")
    worst_3 = sorted(item)[0:3]
    for worst in worst_3:
        worst_index = np.where(item == worst)[0][0]
        print("{}({})".format(contents_df["title"][worst_index+1], worst))

    print("\n")

これで簡易的に結果が出力されます。

似ているほうだけを見やすく整形して表示します。

#結果を格納するデータフレームの作成
best_df = pd.DataFrame(columns=["title", "1", "2", "3"])

for index, item in enumerate(cs_array):
    #その記事のタイトルを最初の要素とするリストの作成
    results = [titles[index+1]]

    #似ている3つ
    best_3 = sorted(item, reverse=True)[1:4]
    for best in best_3:
        best_index = np.where(item == best)[0][0]
        result = "{}({})".format(contents_df["linked_title"][best_index+1], round(best, 3))
        results.append(result)

    #データフレームに追加
    best_df.loc[len(best_df)+1] = results

#ブログ記事執筆用にhtmlタグを出力
print(best_df.set_index("title").to_html(escape=False))

#jupyter notebook内での確認用に整形された表を出力
HTML(best_df.set_index("title").to_html(escape=False))

結果は以下です。

title 1 2 3
ジェンソン・バトン A.J.フォイト(0.222) サンインロー(0.065) 松平康英(0.046)
A.J.フォイト ジェンソン・バトン(0.222) サンインロー(0.068) コロンビアの戦い(0.036)
ブカレスト市電 コラディア・コンチネンタル(0.299) 茨城県道168号静常陸大宮線(0.075) 第56回国民体育大会(0.057)
矢野まき 甲本ヒロト(0.29) 松平康英(0.044) コロンビアの戦い(0.038)
ホオジロ科 ワレカラ(0.037) コラディア・コンチネンタル(0.014) ブカレスト市電(0.009)
コラディア・コンチネンタル ブカレスト市電(0.299) 茨城県道168号静常陸大宮線(0.123) コロンビアの戦い(0.057)
甲本ヒロト 矢野まき(0.29) 心はロンリー気持ちは「…」(0.07) 松平康英(0.051)
ギルドウォーズ コロンビアの戦い(0.04) ジェンソン・バトン(0.035) ワレカラ(0.028)
クィントゥス・マルキウス・ピリップス (紀元前186年の執政官) コロンビアの戦い(0.049) 近代エジプトの国家元首の一覧(0.03) 江畑謙介(0.027)
コロンビアの戦い 茨城県道168号静常陸大宮線(0.064) 松平康英(0.057) コラディア・コンチネンタル(0.057)
ワレカラ コロンビアの戦い(0.045) ブカレスト市電(0.04) ホオジロ科(0.037)
心はロンリー気持ちは「…」 甲本ヒロト(0.07) 松平康英(0.041) 矢野まき(0.035)
潜水空母 江畑謙介(0.068) ブカレスト市電(0.044) コラディア・コンチネンタル(0.03)
サンインロー A.J.フォイト(0.068) ジェンソン・バトン(0.065) ワレカラ(0.032)
近代エジプトの国家元首の一覧 クィントゥス・マルキウス・ピリップス (紀元前186年の執政官)(0.03) ブカレスト市電(0.02) 松平康英(0.015)
松平康英 茨城県道168号静常陸大宮線(0.071) 第56回国民体育大会(0.058) コロンビアの戦い(0.057)
茨城県道168号静常陸大宮線 第56回国民体育大会(0.175) コラディア・コンチネンタル(0.123) ブカレスト市電(0.075)
第56回国民体育大会 茨城県道168号静常陸大宮線(0.175) 松平康英(0.058) ブカレスト市電(0.057)
江畑謙介 潜水空母(0.068) 葉桜が来た夏(0.065) コロンビアの戦い(0.052)
葉桜が来た夏 江畑謙介(0.065) 甲本ヒロト(0.032) 松平康英(0.031)

()内はコサイン類似度を小数第3位にまるめた値です。

その値が高い組み合わせである、ジェンソン・バトンとA.J.フォイト、ブカレスト市電とコラディア・コンチネンタル、矢野まきと甲本ヒロトあたりは納得できる結果です。

何度も繰り返してよさそうに見える結果を示したのではなく、一発勝負でこの結果が得られました。



JanomeとMeCabで日本語のテキストを単語に分ける

テキストの類似度を求める機械学習をするには単語に分ける必要があります。

英語などであればスペースで簡単に分けることができますが、日本語だとそういうわけにはいきません。

そこでjanomeまたはmecabを使って日本語の分割をしてみます。

janomeはインストールが簡単だけれども遅い、mecabはインストールが難しいけれども速いとのことです。

以下では実際に試してみます。

インストール作業は環境により異なるためこの記事では触れません。

 

1.janome

(1)テキストの準備

WikipediaのAPIを使ってランダムに記事本文を取得する – 浅野直樹の学習日記でやったことを関数にしました。

import requests

def get_ml_article():
    #記事ページIDの設定
    page_id = "185375"

    #wikipediaに接続するための基本設定
    S = requests.Session()
    URL = "https://ja.wikipedia.org/w/api.php"

    #記事本体を取得するためのパラメータの設定
    ARTICLE_PARAMS = {
        "action": "query",
        "format": "json",
        "prop": "extracts",
        "explaintext": True,
        "exsectionformat": "plain",
        "pageids": page_id
    }

    #ページIDから記事情報の取得
    ARTICLE_R = S.get(url=URL, params=ARTICLE_PARAMS)
    ARTICLE_DATA = ARTICLE_R.json()
    return ARTICLE_DATA["query"]["pages"][page_id]["extract"]

ml_article = get_ml_article()
print(ml_article)

これで機械学習 – Wikipediaのページの記事本文が取得できました。

 

(2)単純に分かち書き

from janome.tokenizer import Tokenizer

t = Tokenizer(wakati=True)
words = t.tokenize(ml_article)
print(list(words))

これだけです。

['機械', '学習', '(', 'きか', 'い', 'がく', 'しゅう', '、', '英', ':', ' ', 'Machine', ' ', 'Learning', ')', 'と', 'は', '、', '経験',
【以下略】

ともかく分割はできています。

(3)出現回数のカウント

from janome.analyzer import Analyzer
from janome.tokenfilter import *

a = Analyzer(token_filters=[TokenCountFilter(sorted=True)])
words_count = a.analyze(ml_article)
print(list(words_count))

tokenizerの代わりにanalyzerを使います。

[(' ', 427), ('の', 278), ('を', 235), ('、', 216), ('。', 167), ('は', 159), ('で', 146), ('y', 143), ('学習', 137), ('\n ', 130)
【以下略】

出現回数を取得できました。

(4)不要な文字列の除去

「\n」は改行を表わす特殊文字なので除去したいです。スペースも同様です。

「の」や「を」などの助詞、「、」と「。」の句読点はここでは残しておきます(機械学習の直前でストップワードとして除去します)。

from janome.analyzer import Analyzer
from janome.charfilter import *
from janome.tokenfilter import *

char_filters = [UnicodeNormalizeCharFilter(), RegexReplaceCharFilter("\n|\s", "")]
token_filters = [TokenCountFilter(sorted=True)]
a = Analyzer(char_filters=char_filters, token_filters=token_filters)
words_count_cleaned = a.analyze(ml_article)
print(list(words_count_cleaned))

正規表現で改行文字とスペースを除去しています。

[('の', 278), ('を', 235), ('、', 214), ('。', 164), ('は', 159), ('で', 146), ('学習', 137), ('に', 127), ('y', 127), ('(', 121), ('が', 119)
【以下略】

うまくいきました。

 

2.mecab

お次はmecabです。

私の環境ではmecab-python3 · PyPIに書いてある2行のpipだけでインストールできました。

pip install mecab-python3
pip install unidic-lite

登場回数順にカウントするところまで一気にやります。

import MeCab
from collections import Counter

tagger = MeCab.Tagger("-Owakati")
wakati = tagger.parse(ml_article)
print(Counter(wakati.split()).most_common())

以下のように出力されます。

[('の', 290), ('を', 238), ('、', 221), ('に', 194), ('}', 190), ('。', 167), ('は', 162), ('と', 154), ('で', 151), ('y', 143), ('学習', 137),
【以下略】

先ほどのjanomeのときと微妙に数字が違いますね。何も意識せずとも「\n」やスペースは除去されていました。

 

3.janomeとmecabの速度比較

timeitモジュールを使います。

import requests
from janome.analyzer import Analyzer
from janome.charfilter import *
from janome.tokenfilter import *
import MeCab
from collections import Counter
from timeit import timeit

def get_ml_article():
    #記事ページIDの設定
    page_id = "185375"

    #wikipediaに接続するための基本設定
    S = requests.Session()
    URL = "https://ja.wikipedia.org/w/api.php"

    #記事本体を取得するためのパラメータの設定
    ARTICLE_PARAMS = {
        "action": "query",
        "format": "json",
        "prop": "extracts",
        "explaintext": True,
        "exsectionformat": "plain",
        "pageids": page_id
    }

    #ページIDから記事情報の取得
    ARTICLE_R = S.get(url=URL, params=ARTICLE_PARAMS)
    ARTICLE_DATA = ARTICLE_R.json()
    return ARTICLE_DATA["query"]["pages"][page_id]["extract"]

def janome():
    char_filters = [UnicodeNormalizeCharFilter(), RegexReplaceCharFilter("\n|\s", "")]
    token_filters = [TokenCountFilter(sorted=True)]
    a = Analyzer(char_filters=char_filters, token_filters=token_filters)
    words_count_cleaned = a.analyze(ml_article)

def mecab():
    tagger = MeCab.Tagger("-Owakati")
    wakati = tagger.parse(ml_article)
    words_count = Counter(wakati.split()).most_common()

#機械学習 – Wikipediaのページの記事本文を取得
ml_article = get_ml_article()

#timeitで繰り返す回数を設定
loop = 100

#timeitの実行
janome_result = timeit('janome()', globals=globals(), number=loop)
mecab_result = timeit('mecab()', globals=globals(), number=loop)

#実行1回あたりの所要時間の秒数を表示
print("janome:", janome_result / loop)
print("mecab:", mecab_result / loop)

結果は次の通りになりました。

janome: 1.0871145177900325
mecab: 0.025085185560019454

mecabのほうが圧倒的に速いです。

jupyter notebookの%timeitマジックコマンドでも計測してみます。

%timeit -n 10 janome()
1.1 s ± 50.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit -n 10 mecab()
20.2 ms ± 907 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

結果はほぼ同じですね。janomeだと約1秒、mecabだと0.1秒以下です。

 

4.まとめ

速度に関してはmecabの圧勝でした。

janomeにはいろいろと気の利いた機能が備わっていることと、mecabのインストールが思っていたよりはるかに簡単だったのが印象的でした。

janomeもmecabも素晴らしいツールです。

 




top