#15 おすすめの百合漫画紹介 → 機械学習(k近傍法)によるデータ推測

こんにちは、まりなです。百合漫画が好きです。今日はたくさんの百合漫画を所有する私のおすすめの厳選百合漫画15冊を紹介したいと思います。

画像1

まずその15冊を図にまとめてみました。上に行くほど純ラブコメ寄りで下に行くほどギャグ要素強め、左に行くほどプラトニックで右に行くほどえっちです。

ん...?よく考えるとこの図、散布図ですね。

画像2

散布図を見るとk近傍法を使ってラベル推測したくなるな...







ということでおすすめの百合漫画を一冊ずつ紹介していこうかと思っていたのですが気が変わったので機械学習(k近傍法)を用いたラベルの推測を行いたいと思います。

k近傍法とは?

画像3

あるラベル未知の点(クエリ点)の近傍点k個のラベルの中で最多のものから未知のラベルを推測する機械学習の代表的な手法です。

ということで

さっそくやっていきたいと思います。今回上の散布図はExcelを用いて作ったのでせっかくだからそれを活用できるようPythonでxlsxファイルを扱うライブラリopenpyxlを使いたいと思います。Excelで行う作業をPythonで記述し自動化することができてとても便利です。今回は次のような関数を定義しました。

def load_xl(filename, readrng):
   load = op.load_workbook(filename)
   read = load.active

   rng = read[readrng]

   data = np.zeros_like(rng)

   row_num = 0
   for row in rng:
       temp_data = []
       for cell in row:
           temp_data.append(cell.value)
       data[row_num, :] = temp_data
       row_num += 1

   return data

xlsxファイルの名前と読み込み範囲を渡すと行ごとにセルの値を読み込みndarray型の配列にして渡してくれる関数です。

ところでラベルは何にしようか...例えば「値段」のようなラベルは「えっちさ」や「ギャグかラブコメか」と相関がないので今回の場合機械学習で推測することは困難です。「作者」は良い設定でしょうが教師データ(正解データ)の数がどうしても不足してしまいます(誰もがなもり先生のように筆が早く、多くの種類の漫画を出せはしないので)。

色々考えラベルは「雑誌・出版社」にすることにしました。前々から出版社ごとに「色」があるなと思っていたのでそれを検証するいい機会にもなるかと思います。これなら各ラベルにそれなり個数のデータを割り振れそうです。

画像4

ということで教師データにラベルを追加し、数も増やしました。

k近傍法のコードも書きましょう。機械学習用のライブラリを用いると早いでしょうが、今回はその原理的なアルゴリズムにしたがってnumpy以外のライブラリに頼らず作りました。

def Calc_EuclidianDist(vec1, vec2):
   dist = np.linalg.norm(vec1 - vec2)
   return dist

def CalcDist(data, idx1, idx2):
   vec1 = data[idx1]
   vec2 = data[idx2]
   dist = Calc_EuclidianDist(vec1, vec2)
   return dist

def knnSearch(data, query_idx, k):
   if data.shape[0] <= k:
       print("error: dataの個数よりkの値が大きい")
       return

   else:
       dist_list = []
       idx_list = []
       for idx in range(data.shape[0]):
           dist_list.append(CalcDist(data, query_idx, idx))
       
       for num in range(k):
           min_dist = float('inf')
           min_idx = 0
           for idx in range(data.shape[0]):
               if dist_list[idx] < min_dist and query_idx != idx:
                   min_dist = dist_list[idx]
                   min_idx = idx
           idx_list.append(min_idx)
           dist_list[min_idx] = float('inf')

       return idx_list

データの「近さ」を判定する「距離」は今回はシンプルにユークリッド距離を使います。おなじみ三平方の定理で求める普通の「距離」です。Calc_EuclidianDistが2点のユークリッド距離を計算する関数です。CalcDistはデータとデータの2つのインデックスを渡すと距離を計算してくれる関数です。knnSearchがデータとクエリ点のインデックス、kの値を渡すと近傍点k個のインデックスを返してくれる関数です。クエリ点とデータ内の全ての点の距離を計算し、距離の近い点k個を求めることで実装しています(一度選んだ点は距離をfloat('inf') (無限大)にすることで2回選ばないようにしています)。

各関数の呼び出しは次のようなコードで行います。

import numpy as np
import openpyxl as op
from knnsearch import Calc_EuclidianDist, CalcDist, knnSearch
from loadxl import load_xl

def main():
   #教師データの読み込み
   ans_data = load_xl("note.xlsx", "D2:E38")
   #教師データラベルの読み込み
   ans_label = load_xl("note.xlsx", "C2:C38")
   #ラベルシートの読み込み
   labelsheet = load_xl("code.xlsx", "A2:B17")

   k = 3
   query_idx = 36
   label_list = ans_label[knnSearch(ans_data, query_idx, k)]
   
   predict_label = max(label_list)
   
   for label_num in range(labelsheet.shape[0]):
       if predict_label == labelsheet[label_num][1]:
           predict_name = labelsheet[label_num][0]

   print("予測される雑誌・出版社名は「", predict_name, "」")

if __name__ == "__main()__":
    main()

それでは

実行してみようと思います。予測するデータは「私の百合はお仕事です!」(百合姫)にしてみます。

画像5

画像6

実行結果はこちら↓

予測される雑誌・出版社名は「 メディアファクトリー 」

ああーちがう。もう一回。
次は「私、エリート天使ですが難攻不落なJKに困ってます!」(電撃)でやってみます。

画像7

画像8

実行結果はこちら↓

予測される雑誌・出版社名は「 ヤングアニマル 」

うーん違う。
「百合オタに百合はご法度です!?」(アクションコミックス)でもやってみましょう。

画像9

画像10

実行結果↓

予測される雑誌・出版社名は「 ヤングマガジン 」

んー...kの値を調整してみましょう。
k=5にして「きんいろモザイク」(きらら)で試してみます。

画像12

スクリーンショット 2021-03-01 13.27.57

実行結果↓

予測される雑誌・出版社名は「 きらら 」

おお!ついに正解しました。

その後

何度か試しましたが正解率は低かったです。
理由としては次のようなものが考えられます。
1.教師データの数が足りない。
2.座標の設定が100%主観による。

そしておそらく最大の理由が
3.雑誌・出版社と設定したベクトルにあまり相関がない。

散布図(百合姫のみ)

上図は百合姫のデータのみ表示した散布図です。右上にクラスターを形成しているようには見えますがまあまあばらけています。

ということで期待していたほど高い正答率は得られませんでしたが時々正解が出て嬉しかったです。教師データを増やす、次元数を増やすなどの改良の余地はまだまだあるので頑張ってみたいと思います。
それでは、さようなら。

いいなと思ったら応援しよう!