AIで請求項を見える化する

#０．ポイント
１．特許権の権利範囲は請求項として、言葉で表される。
２．請求項は、記載が厳密であるだけに、文章の構造が複雑になることも多く、読み手によっては、誤解が生じる可能性もある。
３．発明者や弁理士、審査官の間で、認識の齟齬が生じないよう、請求項を見える化してみた。
（下の絵は、見える化の一部分です。）

![pic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76072/f9076409-98f6-f815-f6d6-45308e1d31a4.png)

#１．経緯
　知的財産権、なかでも特許権は、新しい時代を切り開く、強力な武器だ。その特許権の効力は、「請求の範囲」に記載された文章（請求項）によって定義される。
　当然のことながら、請求項の一つ一つについて、特許権の構成要素が「必要」「十分」になるよう、厳密な書き方がされているため、複雑な文章構造になることも多い。
　例えば、トヨタが自動運転に関して出願した特許（「自律型車両向けの交通状況認知」、JP2018198422A）の「請求の範囲」は以下の通りとなっている（抜粋）。

```JP2018198422A.txt
車両に備えられた外部センサから、前記車両の外部環境をセンシングして得られたセンサデータを取得する取得ステップと、
前記センサデータを分析して、前記車両の外部における交通状況を識別する識別ステップと、
前記交通状況に関する情報を視覚的に描写する視覚フィードバックを表示させるためのグラフィックデータを生成する生成ステップと、
インタフェース装置に前記視覚フィードバックを表示させるために、前記グラフィックデータを前記インタフェース装置に送信する送信ステップと、
を含む、方法。
```

　何を言っているか、お分かりになるだろうか。最初に読んだとき、正直、僕には、何が何だか、よく分からなかった(笑)。
　読み直してみて、分かったことは、①方法に関する特許であること、②その方法が４つのステップを含むこと、の２点だ。一つ一つのステップの詳細は、正直、読んでいても、「すぐには、頭に入らない」という感じ。もちろん、悪いのは発明ではなく、僕の頭の方だ(笑)。
　そういう状況を踏まえて、請求項を見える化できないか、試しにやってみた。

#２．準備
　さて、見える化だが、AIの定番言語と言って良いPythonを使う。今回は、文章の構造を係り受けとして考えて、Cabochaを利用する。
　まず、準備だが、MeCab、CRF++、Cabocha　の３つが必要。インストール方法は、とっても良い記事があるので、参考に見てみて下さい。僕も参考にさせて頂きました。サイト作成者の方には、この場を借りて、御礼を申し上げます。
[Google Colab で MeCab と CaboCha を使う最強の方法](https://qiita.com/tomowarkar/items/b6a89145c06956618542)
[▲心くじけず言語処理100本ノック＝＝5章下準備＝＝](https://ds-blog.tbtech.co.jp/entry/2020/06/08/%E2%96%B2%E5%BF%83%E3%81%8F%E3%81%98%E3%81%91%E3%81%9A%E8%A8%80%E8%AA%9E%E5%87%A6%E7%90%86100%E6%9C%AC%E3%83%8E%E3%83%83%E3%82%AF%EF%BC%9D%EF%BC%9D5%E7%AB%A0%E4%B8%8B%E6%BA%96%E5%82%99%EF%BC%9D%EF%BC%9D)

　形態素解析のMeCabが正しくインストールできれば、以下の通りとなる。

```python
import MeCab
tagger = MeCab.Tagger()
print(tagger.parse("隣の客はよく柿食う客だ"))
```

```python:output
隣	トナリ	トナリ	隣り	名詞-普通名詞-一般			0
の	ノ	ノ	の	助詞-格助詞			
客	キャク	キャク	客	名詞-普通名詞-一般			0
は	ワ	ハ	は	助詞-係助詞			
よく	ヨク	ヨク	良く	副詞			1
柿	カキ	カキ	柿	名詞-普通名詞-一般			0
食う	クー	クウ	食う	動詞-一般	五段-ワア行	連体形-一般	1
客	キャク	キャク	客	名詞-普通名詞-一般			0
だ	ダ	ダ	だ	助動詞	助動詞-ダ	終止形-一般	
EOS
```

　また、係り受け分析に必要なCRF++、Cabochaが正しくインストールされれば、以下の通りとなる。

```python
import CaboCha
cp = CaboCha.Parser()
print(cp.parseToString("隣の客はよく柿食う客だ"))
```

```python:output
隣の-D        
  客は-------D
    よく---D |
        柿-D |
        食う-D
          客だ
EOS
```

#３．データの加工
　さて、インストールが出来たところで、データを加工しよう。
　まずは、用意したテキストデータを読み込み、各行ごとに形態素解析を行う。それから、Cabochaを使って、係り受け分析をする。

```python
file_path = 'JP2018198422A.txt'
#空のリストの用意
c_list = []
c = CaboCha.Parser()
#テキストデータの読み込み
with open(file_path) as f:
  text_list = f.read()
  #改行で切り分けて各行ごとに形態素解析を行います。
  for i in text_list.split('\n'):
    cabo = c.parse(i)
    #用意したc_listに格納します。
    c_list.append(cabo.toString(CaboCha.FORMAT_LATTICE))
```

　結果をファイルに保存し、データの加工は終了。

```python
#書き出し
path_w = 'JP2018198422A.txt.cabocha'
#リスト型を書き込むときはwritelines()
with open(path_w, mode='w') as f:
  f.writelines(c_list)
```

#４．見える化に挑戦

　それでは、いよいよ、見える化に挑戦する。
　まずは、係り受け分析の結果を読み込む。

```python
#係り受け分析の結果データの読み込み
path = 'JP2018198422A.txt.cabocha'
import re
with open(path, encoding='utf-8') as f:
  _data = f.read().split('\n')
```

　次に、形態素を表すクラスMorphを実装する。このクラスは表層形（surface）、基本形（base）、品詞（pos）、品詞細分類１（pos1）をメンバ変数に持つ。

```python
class Morph:
  def __init__(self, word):
    self.surface = word[0]
    self.base = word[7]
    self.pos = word[1]
    self.pos1 = word[2]
#一文ずつにまとめたリスト
sent = []
#sentに入れるリストの仮置き場
temp = []

for line in _data[:-1]:
  #リスト内の各要素を分割します。
  #集合[]で「\t」と「,」と「　(スペース)」を指定します。
  text = re.split("[\t, ]", line)
  #「EOS」を目印に1文ごとにリストにまとめます。
  if text[0] == 'EOS':
    sent.append(temp)
    #次の文に使用するために空にします。
    temp = []
  #係り受け解析の行は今回は不要なのでcontinue
  elif text[0] == '*':
    continue
  #形態素解析の結果から指定の要素をMorphオブジェクトのリストとしてtempに格納します。
  else:
    morph = Morph(text)
    temp.append(morph)
```

　それから、文節を表すクラスChunkを実装する。このクラスは形態素（Morphオブジェクト）のリスト（morphs）、係り先文節インデックス番号（dst）、係り元文節インデックス番号のリスト（srcs）をメンバ変数に持つ。

```python
#クラスChunk
class Chunk:
  def __init__(self, idx, dst):
    self.idx = idx     #文節番号
    self.morphs = []   #形態素（Morphオブジェクト）のリスト
    self.dst = dst     #係り先文節インデックス番号
    self.srcs = []     #係り元文節インデックス番号のリスト

import re
#1文ごとのリスト
s_list = []
#Chunkオブジェクト
sent = []
#形態素解析結果のMorphオブジェクトリスト
temp = []
chunk = None
for line in _data[:-1]:
  #集合[]で「\t」と「,」と「　(スペース)」を区切りを指定します。
  text = re.split("[\t, ]", line)

  #係り受け解析の行の処理
  if text[0] == '*':
    idx = int(text[1])
    dst = int(re.search(r'(.*?)D', text[2]).group(1))
    #Chunkオブジェクトへ
    chunk = Chunk(idx, dst)
    sent.append(chunk)

  #EOSを目印に文ごとにリスト化
  elif text[0] == 'EOS':
    if sent:
      for i, c in enumerate(sent, 0):
        if c.dst == -1:
          continue
        else:
          sent[c.dst].srcs.append(i)
      s_list.append(sent)
    sent = []
  else:
    morph = Morph(text)
    chunk.morphs.append(morph)
    temp.append(morph)

#1行目の表示
for m in s_list[0]:
  print(m.idx, [mo.surface for mo in m.morphs], '係り元：' + str(m.srcs),'係り先：' + str(m.dst))
```

```python:output
0 ['車両', 'に'] 係り元：[] 係り先：1
1 ['備え', 'られ', 'た'] 係り元：[0] 係り先：2
2 ['外部', 'センサ', 'から', '、'] 係り元：[1] 係り先：8
3 ['前記', '車両', 'の'] 係り元：[] 係り先：4
4 ['外部', '環境', 'を'] 係り元：[3] 係り先：5
5 ['センシング', 'し', 'て'] 係り元：[4] 係り先：6
6 ['得', 'られ', 'た'] 係り元：[5] 係り先：7
7 ['センサデータ', 'を'] 係り元：[6] 係り先：8
8 ['取得', 'する'] 係り元：[2, 7] 係り先：9
9 ['取得', 'ステップ', 'と', '、'] 係り元：[8] 係り先：-1
```

　さらに、係り元の文節と係り先の文節のテキストを抽出する。

```python
for s in s_list:
  for m in s:
    #係り先がある文節の場合、
    if int(m.dst) != -1:
      #形態素解析結果のposが'記号'以外のものをrタブ区切りで表示します。
      print(''.join([b.surface if b.pos != '記号' else '' for b in m.morphs]),
            ''.join([b.surface if b.pos != '記号' else '' for b in s[int(m.dst)].morphs]), sep='\t')
```

```python:output
車両に	　　　　　　　備えられた
備えられた　　　　　　外部センサから
外部センサから　　　　取得する
前記車両の　　　　　　外部環境を
外部環境を　　　　　　センシングして
センシングして　　　　得られた
得られた　　　　　　　センサデータを
センサデータを　　　　取得する
取得する　　　　　　　取得ステップと
前記センサデータを　　分析して
分析して　　　　　　　識別する
前記車両の　　　　　　外部における
外部における　　　　　交通状況を
交通状況を　　　　　　識別する
識別する　　　　　　　識別ステップと
前記交通状況に関する　情報を
情報を　　　　　　　　描写する
視覚的に　　　　　　　描写する
描写する　　　　　　　視覚フィードバックを
視覚フィードバックを　表示させる
表示させる　　　　　　ための
ための　　　　　　　　グラフィックデータを
グラフィックデータを　生成する
生成する　　　　　　　生成ステップと
インタフェース装置に　表示させる
前記視覚フィードバックを表示させる
表示させる	　　　　　ために
ために	　　　　　　　送信ステップと
前記グラフィックデータを　送信する
前記インタフェース装置に	送信する
送信する	　　　   送信ステップと
を                  含む
含む                方法
```

　最後に、係り受け木を有向グラフとして可視化する。見やすさを優先して、最初のステップのみ、見える化する。

```python
#最初の部分を試しにやってみます
v = s_list[0]

#文節のセットを格納するリストの作成
s_pairs = []
for m in v:
  if int(m.dst) != -1:      
    a = ''.join([b.surface if b.pos != '記号' else '' for b in m.morphs])
    b = ''.join([b.surface if b.pos != '記号' else '' for b in v[int(m.dst)].morphs])
    c = a, b
    s_pairs.append(c)
#係り受け木の描画
import pydot_ng as pydot
img = pydot.Dot(graph_type='digraph')
#日本語に対応しているフォントを指定します
img.set_node_defaults(fontname='Meiryo UI', fontsize='12')
for s, t in s_pairs:
  img.add_edge(pydot.Edge(s, t))
img.write_png('pic')
```

　出来上がりは下の通り。最初に掲載したものと同じものだが、再掲する。


![pic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76072/83a4db9a-e978-e495-3b01-16c7782c265e.png)

　如何だろう。どんなステップなのか、頭に入りやすくなったのではないだろうか。

#５．最後に
　特許権が、基本的には言葉で決まっていることを初めて知った時、正直、驚いた。もちろん、図面も重要な書類であるが、特許になるか否か、また、権利範囲の確定は、直接的には、言葉で表される。そういった事実を考えると、文章構造の見える化は、とても重要だと思う。

　なお、例に使わせていただいたトヨタの自動運転に関する特許は、ステイタスはペンディング。特許になるのは、大変だ。

　コーデイングについては、以下のサイトを参考にさせて頂きました。とっても良いサイトで、大変、勉強になりました。この場を借りて、御礼を申し上げます。
[▲心くじけず言語処理100本ノック＝＝40～44＝＝](https://ds-blog.tbtech.co.jp/entry/2020/06/12/%E2%96%B2%E5%BF%83%E3%81%8F%E3%81%98%E3%81%91%E3%81%9A%E8%A8%80%E8%AA%9E%E5%87%A6%E7%90%86100%E6%9C%AC%E3%83%8E%E3%83%83%E3%82%AF%EF%BC%9D%EF%BC%9D%EF%BC%94%EF%BC%90%EF%BD%9E%EF%BC%94%EF%BC%94%EF%BC%9D)