여기는 바로 Natural Language Processing (NLP)의 세계에요! 인공지능(AI) 분야 중 하나로, 텍스트처럼 구조화되지 않은 데이터를 다루는 데 집중하죠. 컴퓨터가 인간의 언어를 이해하고, 주어진 정보에 맞춰 반응하는 방법을 연구하는 분야라고 할 수 있어요.
분석가들에 따르면, 비즈니스 정보의 80~85%가 텍스트 형태로 존재한다고 해요. 엄청난 양의 텍스트 데이터에서 의미 있는 정보를 뽑아내려면 Computational Linguistics와 텍스트 분석이 꼭 필요하겠죠?
이렇게 많은 텍스트를 분석하려면, 데이터를 저장할 편리한 구조가 필수적이에요. 텍스트 분석에서 중요한 건 단어들 사이의 맥락과 관계를 파악하는 건데요. 추출된 의미를 연결된 방식으로 저장할 수 있는 데이터 저장소가 필요하답니다.
는 Graph Database 플랫폼이에요. 텍스트 본문을 연결하고, 텍스트들이 서로 어떻게 관련되는지 맥락을 설정할 수 있게 도와주죠. 단어, 문장, 문서 모두에 적용할 수 있다는 점! 이렇게 관계를 활용하면 대규모 텍스트에서 빠르게 추론하고 통찰력을 얻을 수 있어서, Neo4j는 NLP에 아주 적합하답니다.
이번 글에서는 그래프가 NLP에서 어떻게 활용될 수 있는지 알아볼 거예요. 텍스트 데이터를 로드하고, NLP를 위해 처리하고, NLP 파이프라인을 실행해서, Knowledge Graph를 Neo4j 데이터베이스에서 추출된 정보로 구축하는 과정을 살펴볼게요. 기존 그래프를 가져와서 그 위에 자연어 쿼리 기능을 추가하는 방법도 알아볼 거고요.
(참고: 혹시 기본적인 NLP 용어가 익숙하지 않다면, 기본 NLP 용어집을 먼저 확인해 보세요! KDNuggets에서 제공하는 자료랍니다.)
Neo4j와 Natural Language Processing
Neo4j로 그래프의 강력한 힘을 활용하면 Natural Language Processing이 훨씬 쉬워져요.
텍스트 요소는 데이터베이스의 Nodes로 저장되고, 단어 간의 연결은 Relationships로 저장된다고 생각하면 이해하기 쉬울 거예요. 태그와 Named Entities는 텍스트의 해당 요소나 단어에 연결된 Nodes로 저장되죠.
준비물은 다음과 같아요:
- Neo4j Graph Database
- Py2neo – Neo4j용 Python 드라이버
- GraphAware 라이브러리(jar 파일)
- Stanford CoreNLP(jar 파일)
- NLTK (Natural Language Toolkit)
- Spacy
- Python 편집기 또는 환경 (예: Jupyter Notebook)
Neo4j에서는 GraphAware 플랫폼 플러그인을 사용해서 Natural Language Processing 기능을 활용할 수 있어요. 이 플러그인은 Stanford CoreNLP와 OpenNLP를 기반으로 하고 있답니다.
이 플러그인을 사용하면 텍스트가 토큰으로 분류되고, 태깅되어 데이터베이스에 Node로 저장돼요. 그리고 태그 발생은 Relationship으로 저장되죠.
Neo4j에서 GraphAware를 사용하면 토큰화, 불용어 제거, NER(Named Entity Recognition) 같은 단계를 선택해서 파이프라인을 만들 수 있어요. 어떤 프로세서를 사용할지 (Stanford CoreNLP 또는 OpenNLP)도 선택할 수 있고요.
코퍼스(기사 Node)에서 파이프라인을 실행하면, 파이프라인은 문장의 요소를 태그로 분리해서 텍스트의 모든 부분에 주석을 달아줘요. 여기서 각 태그는 별도의 Node가 되고, 태그 Relationship은 데이터베이스의 Relationship이 되는 거죠.
데이터베이스 설정
Neo4j 데이터베이스에 코퍼스를 로드하는 과정을 한번 살펴볼까요? 여기서 코퍼스는 BBC 비즈니스 기사 모음이고, 텍스트 파일로 저장되어 있어요. 이걸 데이터베이스에 넣어야겠죠. 그리고 각 기사를 추적할 수 있도록 원본 파일 경로를 모든 기사 Node의 Property로 추가하는 게 좋을 것 같아요.
간단한 Python 프로그램을 사용해서 각 파일의 텍스트를 가져와 Neo4j 데이터베이스에 넣을 수 있어요. 그러려면 Neo4j 데이터베이스에 연결하는 데 사용되는 커뮤니티 드라이버인 py2neo 드라이버 패키지가 필요해요. 데이터베이스 액세스를 위해 드라이버에 인증 정보도 제공해야 하고요.
먼저 py2neo를 사용해서 데이터베이스에 연결해 볼게요.
graph = Graph(host='localhost', user='neo4j',password='password')
tx = graph.begin()
이제 해당 필드에 데이터베이스의 사용자 이름과 비밀번호를 입력하고, 모든 기사를 가져와 데이터베이스에 넣어볼게요.
for filename in glob.glob(folder_path):
with open(filename, 'r') as f:
file_contents = f.read()
Nodes = Node("Article",Text=str(file_contents),path=filename)
print(Nodes)
graph.create(Nodes)
tx.merge(Nodes)
폴더 경로는 glob() 함수에 지정해야 해요.
Node() 함수에서는 라벨을 지정해요. 여기서는 “Article”이라고 지정했죠. 문서의 텍스트는 “Text”라는 Property로, 파일 경로는 “path”라는 Property로 Node에 추가돼요.
graph.create()는 모든 Node가 포함된 그래프를 생성하고, tx.merge()는 실제로 데이터베이스에 로드하는 역할을 해요.
이제 데이터베이스로 돌아가서 텍스트가 잘 로드되었는지 확인해 볼까요?
빙고! Neo4j 데이터베이스에 기사가 Node로 잘 들어갔네요!
이제 텍스트에 대해 수행되는 일련의 작업인 Natural Language Processing 파이프라인을 만들어야 해요. GraphAware는 Cypher를 사용해서 토큰화, 엔터티 인식, 의존성 구문 분석과 같은 텍스트에 대한 다양한 작업을 수행할 수 있는 기능을 제공하고 있어요.
파이프라인을 만드는 방법은 다음과 같아요.
CALL ga.nlp.processor.addPipeline({
name:"pipeline_name",
textProcessor: 'com.graphaware.nlp.processor.stanford.ee.processor.EnterpriseStanfordTextProcessor',
processingSteps: {tokenize:true, ner:true, dependencies:true, relations:true, open:true, sentiment:true}
})
GraphAware 문서를 참고해서 Neo4j용 GraphAware 라이브러리의 다양한 메서드를 살펴보고 이해해 보세요.
이제 파이프라인이 잘 생성되었는지 확인해 볼까요?
CALL ga.nlp.processor.getPipeline()
다음과 같은 이름으로 파이프라인이 생성되어야 해요. pipeline” 그리고 모든 단계가 포함되어 있죠.
이제 파이프라인이 있으니, 파이프라인을 통해 모든 텍스트를 실행하고 토큰화하고 태깅해 봅시다. 그래프의 구조는 "무엇이 어디로 가는지"를 관계로 저장할 수 있다는 장점이 있고, 조회에 정말 유용해요.
우리가 가진 모든 텍스트에 주석을 달기 위해 주석 기능을 실행해 볼게요. 더 큰 크기의 데이터에는 APOC Library의 함수를 사용하는 게 좋아요. Neo4j(apoc. periodic.iterate)에서 더 빠른 결과를 얻을 수 있거든요.
CALL apoc.periodic.iterate(
'MATCH (n:Article) RETURN n',
'CALL ga.nlp.annotate({
text: n.Text,
id: id(n),
pipeline: "pipeline_name",
checkLanguage:false
})
YIELD result MERGE (n)-[:HAS_ANNOTATED_TEXT]->(result)',
{batchSize:1, iterateList:false})
이렇게 하면 텍스트가 분해되고, 해당 ID로 주석이 추가되며, 필요한 관계가 생성돼요.
위 이미지에서는 article (분홍색 Node)이 단어 단위로 더 세분화된 문장으로 분기되는 것을 볼 수 있어요 (빨간색 Nodes). 이는 기사의 모든 문장과 모든 후속 기사 Node에 대해 발생하죠.
전체 텍스트를 여러 부분으로 분해한 후, Cypher 쿼리를 사용해서 텍스트에서 통찰력을 얻을 수 있는지 살펴볼까요?
이 기사에서 가장 많이 언급된 상위 5개 조직을 살펴봅시다.
MATCH (n:NER_Organization)
RETURN n.value, size((n)<-[:HAS_TAG]-()) as Frequency
ORDER BY Frequency DESC
LIMIT 5
가장 많이 언급된 상위 5명에 대해서도 똑같이 해 볼게요.
MATCH (n:NER_Person)
RETURN n.value, size((n)<-[:HAS_TAG]-()) as Frequency
ORDER BY Frequency DESC
LIMIT 5
부정적인 맥락에서 가장 많이 언급된 회사를 확인할 수도 있어요.
MATCH (tag:NER_Organization)-[]-(s:Sentence:Negative)-[]-(:AnnotatedText)-[]-(a:Article)
WHERE NOT tag:NER_Person AND NOT tag:NER_O
RETURN distinct tag.value, count(a) as articles
ORDER BY articles DESC;
Knowledge Graph를 구축하는 과정에서 누가 어디서, 어떤 역량이나 역할로 일하는지에 대한 몇 가지 정보를 추출하게 돼요.
MATCH (o:NE_Organization)-[]-(p:NE_Person)-[]-(t:TagOccurrence)
WHERE NOT t:NE_Person AND t.pos IN [['NN']]
RETURN DISTINCT p.value AS Person, collect(distinct t.value) as Title, o.value AS Company
ORDER BY Company
이러한 통찰력을 사용해서 의미 있는 관계를 구축할 수 있으므로, 실제로 Knowledge Graph를 구축하는 방향으로 나아갈 수 있어요.
Neo4j 데이터베이스에서 Natural Language Processing 검색을 수행하면 일반 텍스트 뉴스 기사를 Knowledge Graph로 변환할 수 있다는 점도 기억해주세요. 510개의 비즈니스 뉴스 기사를 의미 있는 통찰력으로 바꿀 수 있는 Neo4j, 정말 매력적이지 않나요!
첫 번째 단계에서는 원시 텍스트에서 정보를 추출해서 Knowledge Graph를 만들고 조각들을 하나로 묶었어요. Knowledge Graph는 링크를 통해서 모든 정보를 통합하고, 추론자가 데이터에서 새로운 지식을 도출할 수 있도록 도와주죠.
Neo4j를 위한 Natural Language Query
Knowledge Graph의 주요 응용 프로그램 중 일부는 대화형 인터페이스와 데이터베이스에 대한 Natural Language Search에요. 다음 섹션에서는 데이터베이스 및 Knowledge Graph에 대한 Natural Language Query 기능을 구축해 볼 거예요.
질문을 그래프에 매핑하려면 질문을 원자 요소로 분해해야 해요. Natural Language Processing (NLP) 용 SpaCy를 사용해서 Python에서 이 작업을 수행하고, py2neo 드라이버를 사용해서 그래프에 연결할 거에요.
그래프에서 수행한 작업과 매우 유사하게 NLP 파이프라인을 통해 질문을 실행해서 태그를 생성하고, 질문과 관련된 단어에 관한 언어 식별자를 받게 돼요. 질문에서 키워드를 식별하면 정확히 질문된 내용을 찾는 데 도움이 되죠.
먼저 py2neo를 사용해서 Neo4j 데이터베이스에 연결해야 해요.
graph = Graph("bolt://localhost:7687",auth = ("username","password"))
tx = graph.begin()
실제로 질문을 입력하고 작업해 볼게요. 데모를 위해 "래리 엘리슨은 어디에서 일하나요?"라는 질문을 던져보겠습니다.
여기에서 SpaCy는 질문을 토큰화하고 해당 내용을 언어 식별자에 태그해요. 각각의 질문에서 생성된 이러한 토큰은 식별자 세트 역할을 하며, 시스템에 그래프에서 적합한 Node를 찾도록 지시하죠.
아래 이미지는 질문을 단어로 분류하고 식별자로 태그를 지정한 모습이에요.
다음 코드 블록에서는 토큰화, 태그 지정, 명명된 엔터티 인식 및 종속성 구문 분석을 수행하고, 이를 위의 다이어그램으로 렌더링하는 질문에 대해 NLP 파이프라인을 실행하고 있어요.
nlp = en_core_web_sm.load()
doc = nlp(question)
ner = [(X.text, X.label_) for X in doc.ents]
print(filtered_question)
for token in doc:
print((token.text, token.pos_, token.tag_, token.dep_))
print(ner)
displacy.render(doc)
Cypher 쿼리에서 매개변수로 사용해야 하는 질문에서 인식된 명명된 엔터티가 있어요. 이를 위해 질문에 명명된 엔터티가 포함된 경우, 해당 엔터티 유형(예: 사람, 조직, 위치)에 따라 해당 엔터티를 매개 변수에 넣는 로직을 사용할 수 있어요.
아래 코드 블록은 명명된 엔터티가 두 개 이상 있는지 또는 명명된 엔터티 유형이 조직인지 확인해요. 그런 다음 이를 키-값 쌍에 넣죠.
if (len(ner[0])>2):
parms_2 = {"name1":name1, "name2":name2}
print(parms_2)
else:
parms = {}
parms["names"]=names
print("Parms: ",parms)
if (ner[0][1] == "ORG"):
parms_1 = {}
parms_1["org"] = [ner[0][0]]
print(parms_1)
이는 다음 Cypher 쿼리에서 매개변수로 사용될 거예요.
태그된 이름은 다음과 같아요: [['Larry', 'Ellison']]
Parms: {'이름': ['Larry Ellison']}
앞서 언급했듯이 질문에서 생성된 토큰 세트를 사용해서 적절한 매개변수를 입력하여 사용하려는 Cypher 쿼리를 식별하고 구성해요.
여기에서 "Larry Ellison" 매개변수의 경우 Cypher 쿼리는 다음과 같아요.
MATCH (p:Person) -[r:WORKS_AT]-> (o:Organization)
WHERE p.value = "Larry Ellison"
query1 = '''
match {} {} {}
where p.value IN $person
return p.value as Name,r.value as Works_as,o.value as at
'''.format(label_p,works,label_o)
마지막으로 아래에 표시된 쿼리를 실행해 볼게요. graph.run()은 py2neo의 기능으로 결과를 만들어내죠. 텍스트 형식으로 간단한 결과 테이블이 생성되지만, 이 쿼리를 가져다가 Neo4j Browser에서 실행해서 관계를 시각화하는 걸 추천드려요.
for word,tag in tags:
if word in ['work','do']:
verb = word
print('We have a verb: ',verb)
print(graph.run(query1,parms).data())
동사 work가 있으니, 우리가 요청한 질문에서 쿼리에서 적절한 관계를 선택해서 해당 동사를 중심으로 쿼리를 구성할 수 있어요.
만약 'n'명의 사람이 포함된 Knowledge Graph가 있다면, 이 쿼리를 활용해서 모든 사람에 대해 동일한 정보를 얻을 수 있어요. 올바른 쿼리를 디자인하면 Knowledge Graph에 대한 거의 모든 질문에 답할 수 있다는 의미죠!
Neo4j 데이터베이스 내에서 전체 NLP 작업 세트를 실행한 다음 가치 있는 것을 추출하고 생성할 수도 있지만, 이미 사용 가능한 Knowledge Graph를 가져와서 NLP를 사용해서 자연어 인터페이스로 쿼리할 수도 있어요.
그 방법을 한번 살펴볼까요!
Neo4j 위에 NLP
이번 예제에서는 Neo4j 데이터베이스 내에서 NLP 파이프라인을 실행하지 않을 거예요. 대신, Python에서 NLP 라이브러리를 사용해서 자연어에 가까운 쿼리 기능을 구축할 건데요, ICIJ의 파나마 논문 데이터 세트가 Neo4j에 로드되어 있다고 가정할게요. Panama Papers 데이터세트에는 기업별 해외 투자 기록과 이러한 투자와 관련된 다양한 조직의 역할이 포함되어 있죠.
기본적으로 우리는 질문에 대해 NLP 파이프라인을 실행하고 질문의 토큰을 활용해서 원하는 Cypher 쿼리를 생성할 거예요. 여기서 목표는 기존 Graph Database 위에 대화형 시스템을 만들고, 가능하다면 Graph Algorithms, 예를 들어 PageRank 같은 알고리즘을 활용해서 그래프에서 가장 영향력 있는 엔터티를 가져오는 거예요.
질문의 토큰을 기반으로 문자열 작성을 사용해서 쿼리를 작성하기 위해 간단한 조건부 프로그래밍 패러다임을 구현했어요.
먼저 Neo4j 데이터베이스에 연결해 볼게요.
def connect():
graph = Graph("bolt://localhost:7687",auth = ("username","password"))
tx = graph.begin()
print('Connected...')
그런 다음 사용자에게 질문을 입력하도록 요청할 거예요. 이 예에서는 '파나마 관할권에 속하는 위치는 어디입니까?'라고 질문해 볼게요.
def ask_question():
question = input("INPUT: ")
print("n")
이제 질문이 생겼으니, 이에 대해 몇 가지 NLP 기능을 실행해 보죠.
먼저 질문을 토큰화하고 태그를 지정할게요. 아래 이미지는 질문을 단어로 분류하고 식별자로 태그를 지정한 결과를 보여줘요. 다음과 같이 표시되죠.
이전 질문에 대해 했던 것과 유사하게 아래 코드에서는 토큰화, 태그 지정, 명명된 엔터티 인식 및 종속성 구문 분석을 수행하고 이를 위 다이어그램으로 렌더링하기 위한 NLP 파이프라인을 실행하고 있어요.
def tag_question():
doc = nlp(question) tokens = [token.text for token in doc]
pos = [pos.pos_ for pos in doc]
tags = zip(tokens,pos)
tags = list(tags)
ner = [(ner.text,ner.label_) for ner in doc.ents]
다음으로, Cypher 쿼리에서 사용하려면 질문의 명명된 엔터티를 기반으로 매개 변수를 작성해야 해요.
def parms_builder():
if len(ner) == 1:
if (ner[0][1] == 'GPE') or (ner[0][1] == 'LOC'):
if (ner[0][0] == "US") or (ner[0][0] == "USA"):
country_ = 'United States'
elif (ner[0][0] == "UK"):
country_ = 'United Kingdom'
else:
country = ner[0][0]
parms = {}
parms["country"] = country
이제 토큰과 매개변수가 있으니, 이를 기반으로 Cypher 쿼리 작성을 시작할 수 있어요. 질문에서 최소한 두 개의 고유 토큰 세트를 선택하고 이를 조건에 활용해서 쿼리를 구성해 보세요. 우리는 또한 any() and all() 토큰 목록의 요소를 확인하는 Python의 함수를 사용할 거예요.
루프에 들어가면 검색할 Node와 Relationship을 지정하고 이를 문자열 작성기에 매개변수로 전달해야 해요.
마지막으로 우리는 graph.run() Cypher 쿼리를 실행하고 결과를 생성하는 py2neo의 함수를 사용할 거예요:
if token in (all(['come','under']) and ['jurisdiction']):
match_0 = "MATCH {}".format(label_entity)
query = match_0 + "WHERE entity.jurisdiction_description CONTAINS $country RETURN collect(distinct entity.countries) as Locations, entity.jurisdiction_description as Jurisdiction"
print(graph.run(query,parms).to_table())
아래 표에서 우리는 이러한 위치의 기업이 파나마를 선택한 관할권으로 사용했음을 나타내는 많은 국가가 있음을 분명히 알 수 있어요. (참고: 더 읽기 쉽게 출력 형식이 변경되었어요.)
이 시스템은 쿼리를 가져오는 규칙이 Machine Learning(ML)을 사용하지 않고 수동으로 프로그래밍되므로 질문할 수 있는 질문 유형의 범위가 제한되어 있어요. 하지만 질문의 수와 유형을 늘리면 모델의 범위를 늘리는 것이 가능하죠.
Node를 자동으로 감지하기 위해 샘플에서 ML 접근 방식을 시도했지만 이 애플리케이션에서 사용하기에 충분한 정확도를 얻지 못했어요. 하지만 어휘가 많을수록 정확도가 향상될 수 있을 거예요.
앞으로 나아가다
앞으로 나아갈 길은 동적 문자열 구축을 동적 Cypher 쿼리 구축으로 바꾸거나 질문에서 Cypher 쿼리를 자동으로 구축하는 것이에요. 이를 위해서는 최상의 결과를 생성하기 위해 복잡한 모델링 기술과 적절한 훈련-테스트 데이터 세트가 필요하죠.
Octavian의 블로그 게시물 시퀀스 번역에 관한 책은 더 깊은 기술적 관점을 얻고자 하는 사람들에게 좋은 읽기 자료가 될 거예요. IEEE(Institute of Electrical and Electronics Engineers)에서 발표한 “보정된 양자 메시를 사용한 인지 자연어 검색”이라는 또 다른 논문도 Natural Language Processing에 그래프 구조를 활용하고 있어요.
다시 말하면, 그래프 모델의 구조는 Natural Language Processing을 더 쉽게 만들어요. 그래프에는 원시 데이터를 Knowledge Graph와 같은 정보로 변환하는 힘이 있으며, 이러한 능력으로 인해 Neo4j는 AI에서 계속 증가하는 문제를 해결하기 위한 좋은 후보가 될 거예요.
이 블로그 게시물에 사용된 모든 코드와 예제에 사용된 데이터는 다음에서 찾을 수 있어요. .
백서를 읽고,인공 지능 및 그래프 기술: 컨텍스트 및 연결을 통해 AI 향상
- APOC
- Natural Language Processing
- nlp
에이치시스템즈의 LogTree는 Neo4j 기반 GraphRAG 플랫폼으로, 데이터를 자동으로 지식그래프화하고 자연어 질의로 즉시 답을 제공합니다.
'Neo4j' 카테고리의 다른 글
| Neo4j로 압도적인 속도와 확장성을 경험하세요 (0) | 2026.09.03 |
|---|---|
| 그래프 데이터베이스 방식으로 풀어보는 접근 제어 목록 (Access Control List) (0) | 2026.09.03 |
| 2020년 Neo4j, 가장 핫했던 9가지 뉴스 & 발표 총정리! (0) | 2026.09.02 |
| Bolt 프로토콜, 최신 업데이트 소식! (0) | 2026.09.02 |
| 임시 Neo4j 데이터베이스를 위한 라이브러리 (0) | 2026.09.01 |
