1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

オントロジーのグラフデータベース化

Posted at

はじめに

SUMO(Suggested Upper Merged Ontology) を neo4j に変換する。

Kobito.A79jKQ.png

SUMO

クラス・インスタンス階層や一階述語論理の機構を備えたオントロジー。kifという言語で記述されている。

(subclass Physical Entity)
(partition Physical Object Process)

以下のページの Download から zip ファイルでダウンロードする。

使用するgem

Rubyからneo4jを扱うライブラリとしてneographerやneo4jがあるが、neographerがいまいちよく分からなかったのでneo4jを使う。kifファイルをパースするためにsxpを使う。zipファイルを読むためにrubyzipを使う。

sudo gem install neo4j
sudo gem install sxp
sudo gem install rubyzip

kifファイルを読む

とりあえずkifファイルを読むモジュールを書く。
kifはS式の一種なので、S式の文字列を読んで配列にしてくれるsxpを使用する。

SXP.read '((subclass Cat Animal) (instance Tama Cat))'
=> [[:subclass, :Cat, :Animal], [:instance, :Tama, :Cat]]

コメント行の削除などの掃除をしてから、SXPに食わせる。

require 'sxp'

module KIF
	module_function
	def parse(file)
		doc = file.read.force_encoding('UTF-8')
		# BOM削除
		doc = doc[1..-1] if doc[0].ord == 65279
		# コメント削除
		_doc = ''
		comment = false
		quote = false
		doc.split(/(\\"|"|;|\n)/).each do |item|
			if comment
				comment = false if item == "\n"
			elsif quote
				quote = false if item == '"'
				_doc += item
			else
				comment = true if item == ';'
				quote = true if item == '"'
				_doc += ' ' if item == '"'  # ComputerInput.kif のバグ対策
				_doc += item unless comment
			end
		end
		# S式
		doc = '(' + _doc + ')'
		sxp = SXP.read(doc)
		return sxp
	end
end

zipファイルから読み込む

zipファイルを展開してから1ファイルずつ処理してもいいのだが、かさ張るのでzipファイルのまま読むことにする。

require 'zip'

class KIF::Archive
	def initialize(path)
		@path = path
	end
	def each
		Zip::File.open(@path) do |zip|
			zip.glob('sumo-master/*.kif').each do |entry|
				yield entry
			end
		end
	end
end

kifファイルのうち、WorldAirports.kif などは項目数は多いがほとんど使わないので除外する。

sumo = KIF::Archive.new('sumo-master.zip')
sumo.each do |entry|
	next if entry.name =~ /WorldAirports/
	sxp = entry.get_input_stream{|f| KIF.parse(f) }
end

neo4j に書き込む

neo4jに書き込むための writer クラスを定義。

require 'neo4j-core'

class Writer
	def create_index
		Neo4j::Label.create('class').create_index(:name)
		Neo4j::Label.create('instance').create_index(:name)
	end
	def write(sxp)
		@nodes ||= Hash.new
		sxp.each do |axiom|
			next if axiom.any?{|item| item.is_a? Array }
			predicate, *arguments = *axiom
			case predicate
			when :subclass
				target, source = *arguments
				source_label = 'class'
				target_label = 'class'
			when :instance
				target, source = *arguments
				source_label = 'class'
				target_label = 'instance'
			when :subrelation, :subAttribute
				target, source = *arguments
				source_label = 'instance'
				target_label = 'instance'
			when :domain, :domainSubclass
				source, order, target = *arguments
				source_label = 'instance'
				target_label = 'class'
			when :range, :rangeSubclass
				source, target = *arguments
				source_label = 'instance'
				target_label = 'class'
			end
			next unless source
			@nodes[source] ||= Neo4j::Node.create({name: source}, source_label)
			@nodes[target] ||= Neo4j::Node.create({name: target}, target_label)
			relation = @nodes[source].create_rel(predicate, @nodes[target])
			relation[:order] = order if order
		end
	end
end

create_indexはノードのname属性をインデックスにする設定のためのメソッド。writeがS式を受け取ってデータベースに書き込むメソッド。ここでは、kifから以下の式のみを取り出してデータベースに書き込んでいる。

(subclass target source)
(instance target source)
(subrelation target source)
(subAttribute target source)
(domain source order target)
(domainSubclass source order target)
(range source target)
(rangeSubclass source target)

domainというのは定義域のことで、要するに項の型を指定している。

(domain instance 1 Entity)
(domain instance 2 Class)

rangeというのは値域のことで、関数タイプの述語に対して返り値の型を指定する。

(range whereFn Region)

グラフデータベースは要素と要素を関係で結ぶので、基本的に(関係 要素 要素)という2項述語しか表現できない。domainとdomainSubclassは3項述語だが、order項をリレーションの属性とすることで表現している。

実行

WorldAirports.kif などは項目数ばかり多くてほとんど使わないので除外し、他のものをデータベースに書き込む。

sumo = KIF::Archive.new('sumo-master.zip')
session = Neo4j::Session.open(:server_db, 'http://localhost:7474', basic_auth: {username: 'myname', password: 'mypassword'})
writer = Writer.new
writer.create_index
sumo.each do |entry|
	STDERR.puts entry.name
	next unless entry.name !~ /WorldAirports/
	sxp = entry.get_input_stream{|f| KIF.parse(f) }
	writer.write sxp
end

ToDo

WordNetにはSUMOへのリンクが収録されており、日本語->WordNet->SUMOという経路で語義を引くことができるので、これもデータベースに登録する。とりあえずここまで。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?