In [ ]:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
In [ ]:
#@title MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 공식 영문 문서의 내용과 일치하지 않을 수 있습니다. 이 번역에 개선할 부분이 있다면 tensorflow/docs-l10n 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다. 문서 번역이나 리뷰에 참여하려면 docs-ko@tensorflow.org로 메일을 보내주시기 바랍니다.
자연어 처리 모델은 종종 다른 문자 집합을 갖는 다양한 언어를 다루게 됩니다. 유니코드(unicode)는 거의 모든 언어의 문자를 표현할 수 있는 표준 인코딩 시스템입니다. 각 문자는 0
부터 0x10FFFF
사이의 고유한 정수 코드 포인트(code point)를 사용해서 인코딩됩니다. 유니코드 문자열은 0개 또는 그 이상의 코드 포인트로 이루어진 시퀀스(sequence)입니다.
이 튜토리얼에서는 텐서플로(Tensorflow)에서 유니코드 문자열을 표현하고, 표준 문자열 연산의 유니코드 버전을 사용해서 유니코드 문자열을 조작하는 방법에 대해서 소개합니다. 또한 스크립트 감지(script detection)를 활용하여 유니코드 문자열을 토큰으로 분리해 보겠습니다.
In [ ]:
import tensorflow as tf
In [ ]:
tf.constant(u"Thanks 😊")
tf.string
텐서는 바이트 문자열을 최소 단위로 다루기 때문에 다양한 길이의 바이트 문자열을 다룰 수 있습니다. 문자열 길이는 텐서 차원(dimensions)에 포함되지 않습니다.
In [ ]:
tf.constant([u"You're", u"welcome!"]).shape
노트: 파이썬을 사용해 문자열을 만들 때 버전 2와 버전 3에서 유니코드를 다루는 방식이 다릅니다. 버전 2에서는 위와 같이 "u" 접두사를 사용하여 유니코드 문자열을 나타냅니다. 버전 3에서는 유니코드 인코딩된 문자열이 기본값입니다.
텐서플로에서 유니코드 문자열을 표현하기 위한 두 가지 방법이 있습니다:
string
스칼라 — 코드 포인트의 시퀀스가 알려진 문자 인코딩을 사용해 인코딩됩니다.int32
벡터 — 위치마다 개별 코드 포인트를 포함합니다.예를 들어, 아래의 세 가지 값이 모두 유니코드 문자열 "语言处理"
(중국어로 "언어 처리"를 의미함)를 표현합니다.
In [ ]:
# UTF-8로 인코딩된 string 스칼라로 표현한 유니코드 문자열입니다.
text_utf8 = tf.constant(u"语言处理")
text_utf8
In [ ]:
# UTF-16-BE로 인코딩된 string 스칼라로 표현한 유니코드 문자열입니다.
text_utf16be = tf.constant(u"语言处理".encode("UTF-16-BE"))
text_utf16be
In [ ]:
# 유니코드 코드 포인트의 벡터로 표현한 유니코드 문자열입니다.
text_chars = tf.constant([ord(char) for char in u"语言处理"])
text_chars
In [ ]:
tf.strings.unicode_decode(text_utf8,
input_encoding='UTF-8')
In [ ]:
tf.strings.unicode_encode(text_chars,
output_encoding='UTF-8')
In [ ]:
tf.strings.unicode_transcode(text_utf8,
input_encoding='UTF8',
output_encoding='UTF-16-BE')
여러 개의 문자열을 디코딩 할 때 문자열마다 포함된 문자의 개수는 동일하지 않습니다. 반환되는 값은 tf.RaggedTensor
로 가장 안쪽 차원의 크기가 문자열에 포함된 문자의 개수에 따라 결정됩니다.
In [ ]:
# UTF-8 인코딩된 문자열로 표현한 유니코드 문자열의 배치입니다.
batch_utf8 = [s.encode('UTF-8') for s in
[u'hÃllo', u'What is the weather tomorrow', u'Göödnight', u'😊']]
batch_chars_ragged = tf.strings.unicode_decode(batch_utf8,
input_encoding='UTF-8')
for sentence_chars in batch_chars_ragged.to_list():
print(sentence_chars)
tf.RaggedTensor
를 바로 사용하거나, 패딩(padding)을 사용해 tf.Tensor
로 변환하거나, tf.RaggedTensor.to_tensor
와 tf.RaggedTensor.to_sparse
메서드를 사용해 tf.SparseTensor
로 변환할 수 있습니다.
In [ ]:
batch_chars_padded = batch_chars_ragged.to_tensor(default_value=-1)
print(batch_chars_padded.numpy())
In [ ]:
batch_chars_sparse = batch_chars_ragged.to_sparse()
길이가 같은 여러 문자열을 인코딩할 때는 tf.Tensor
를 입력으로 사용합니다.
In [ ]:
tf.strings.unicode_encode([[99, 97, 116], [100, 111, 103], [ 99, 111, 119]],
output_encoding='UTF-8')
길이가 다른 여러 문자열을 인코딩할 때는 tf.RaggedTensor
를 입력으로 사용해야 합니다.
In [ ]:
tf.strings.unicode_encode(batch_chars_ragged, output_encoding='UTF-8')
패딩된 텐서나 희소(sparse) 텐서는 unicode_encode
를 호출하기 전에 tf.RaggedTensor
로 바꿉니다.
In [ ]:
tf.strings.unicode_encode(
tf.RaggedTensor.from_sparse(batch_chars_sparse),
output_encoding='UTF-8')
In [ ]:
tf.strings.unicode_encode(
tf.RaggedTensor.from_tensor(batch_chars_padded, padding=-1),
output_encoding='UTF-8')
In [ ]:
# UTF8에서 마지막 문자는 4바이트를 차지합니다.
thanks = u'Thanks 😊'.encode('UTF-8')
num_bytes = tf.strings.length(thanks).numpy()
num_chars = tf.strings.length(thanks, unit='UTF8_CHAR').numpy()
print('{} 바이트; {}개의 UTF-8 문자'.format(num_bytes, num_chars))
In [ ]:
# 기본: unit='BYTE'. len=1이면 바이트 하나를 반환합니다.
tf.strings.substr(thanks, pos=7, len=1).numpy()
In [ ]:
# unit='UTF8_CHAR'로 지정하면 4 바이트인 문자 하나를 반환합니다.
print(tf.strings.substr(thanks, pos=7, len=1, unit='UTF8_CHAR').numpy())
In [ ]:
tf.strings.unicode_split(thanks, 'UTF-8').numpy()
In [ ]:
codepoints, offsets = tf.strings.unicode_decode_with_offsets(u"🎈🎉🎊", 'UTF-8')
for (codepoint, offset) in zip(codepoints.numpy(), offsets.numpy()):
print("바이트 오프셋 {}: 코드 포인트 {}".format(offset, codepoint))
각 유니코드 코드 포인트는 스크립트(script)라 부르는 하나의 코드 포인트의 집합(collection)에 속합니다. 문자의 스크립트는 문자가 어떤 언어인지 결정하는 데 도움이 됩니다. 예를 들어, 'Б'가 키릴(Cyrillic) 스크립트라는 것을 알고 있으면 이 문자가 포함된 텍스트는 아마도 (러시아어나 우크라이나어 같은) 슬라브 언어라는 것을 알 수 있습니다.
텐서플로는 주어진 코드 포인트가 어떤 스크립트를 사용하는지 판별하기 위해 tf.strings.unicode_script
연산을 제공합니다. 스크립트 코드는 International Components for
Unicode (ICU) UScriptCode
값과 일치하는 int32
값입니다.
In [ ]:
uscript = tf.strings.unicode_script([33464, 1041]) # ['芸', 'Б']
print(uscript.numpy()) # [17, 8] == [USCRIPT_HAN, USCRIPT_CYRILLIC]
tf.strings.unicode_script
연산은 코드 포인트의 다차원 tf.Tensor
나 tf.RaggedTensor
에 적용할 수 있습니다:
In [ ]:
print(tf.strings.unicode_script(batch_chars_ragged))
분할(segmentation)은 텍스트를 단어와 같은 단위로 나누는 작업입니다. 공백 문자가 단어를 나누는 구분자로 사용되는 경우는 쉽지만, (중국어나 일본어 같이) 공백을 사용하지 않는 언어나 (독일어 같이) 단어를 길게 조합하는 언어는 의미를 분석하기 위한 분할 과정이 꼭 필요합니다. 웹 텍스트에는 "NY株価"(New York Stock Exchange)와 같이 여러 가지 언어와 스크립트가 섞여 있는 경우가 많습니다.
스크립트의 변화를 단어 경계로 근사하여 (ML 모델 사용 없이) 대략적인 분할을 수행할 수 있습니다. 위에서 언급된 "NY株価"의 예와 같은 문자열에 적용됩니다. 다양한 스크립트의 공백 문자를 모두 USCRIPT_COMMON(실제 텍스트의 스크립트 코드와 다른 특별한 스크립트 코드)으로 분류하기 때문에 공백을 사용하는 대부분의 언어들에서도 역시 적용됩니다.
In [ ]:
# dtype: string; shape: [num_sentences]
#
# 처리할 문장들 입니다. 이 라인을 수정해서 다른 입력값을 시도해 보세요!
sentence_texts = [u'Hello, world.', u'世界こんにちは']
먼저 문장을 문자 코드 포인트로 디코딩하고 각 문자에 대한 스크립트 식별자를 찾습니다.
In [ ]:
# dtype: int32; shape: [num_sentences, (num_chars_per_sentence)]
#
# sentence_char_codepoint[i, j]는
# i번째 문장 안에 있는 j번째 문자에 대한 코드 포인트 입니다.
sentence_char_codepoint = tf.strings.unicode_decode(sentence_texts, 'UTF-8')
print(sentence_char_codepoint)
# dtype: int32; shape: [num_sentences, (num_chars_per_sentence)]
#
# sentence_char_codepoint[i, j]는
# i번째 문장 안에 있는 j번째 문자의 유니코드 스크립트 입니다.
sentence_char_script = tf.strings.unicode_script(sentence_char_codepoint)
print(sentence_char_script)
그다음 스크립트 식별자를 사용하여 단어 경계가 추가될 위치를 결정합니다. 각 문장의 시작과 이전 문자와 스크립트가 다른 문자에 단어 경계를 추가합니다.
In [ ]:
# dtype: bool; shape: [num_sentences, (num_chars_per_sentence)]
#
# sentence_char_starts_word[i, j]는
# i번째 문장 안에 있는 j번째 문자가 단어의 시작이면 True 입니다.
sentence_char_starts_word = tf.concat(
[tf.fill([sentence_char_script.nrows(), 1], True),
tf.not_equal(sentence_char_script[:, 1:], sentence_char_script[:, :-1])],
axis=1)
# dtype: int64; shape: [num_words]
#
# word_starts[i]은 (모든 문장의 문자를 일렬로 펼친 리스트에서)
# i번째 단어가 시작되는 문자의 인덱스 입니다.
word_starts = tf.squeeze(tf.where(sentence_char_starts_word.values), axis=1)
print(word_starts)
이 시작 오프셋을 사용하여 전체 배치에 있는 단어 리스트를 담은 RaggedTensor
를 만듭니다.
In [ ]:
# dtype: int32; shape: [num_words, (num_chars_per_word)]
#
# word_char_codepoint[i, j]은
# i번째 단어 안에 있는 j번째 문자에 대한 코드 포인트 입니다.
word_char_codepoint = tf.RaggedTensor.from_row_starts(
values=sentence_char_codepoint.values,
row_starts=word_starts)
print(word_char_codepoint)
마지막으로 단어 코드 포인트 RaggedTensor
를 문장으로 다시 나눕니다.
In [ ]:
# dtype: int64; shape: [num_sentences]
#
# sentence_num_words[i]는 i번째 문장 안에 있는 단어의 수입니다.
sentence_num_words = tf.reduce_sum(
tf.cast(sentence_char_starts_word, tf.int64),
axis=1)
# dtype: int32; shape: [num_sentences, (num_words_per_sentence), (num_chars_per_word)]
#
# sentence_word_char_codepoint[i, j, k]는 i번째 문장 안에 있는
# j번째 단어 안의 k번째 문자에 대한 코드 포인트입니다.
sentence_word_char_codepoint = tf.RaggedTensor.from_row_lengths(
values=word_char_codepoint,
row_lengths=sentence_num_words)
print(sentence_word_char_codepoint)
최종 결과를 읽기 쉽게 utf-8 문자열로 다시 인코딩합니다.
In [ ]:
tf.strings.unicode_encode(sentence_word_char_codepoint, 'UTF-8').to_list()