유니코드 문자열

TensorFlow.org에서 보기 Google Colab에서 실행 GitHub에서 소스 보기 노트북 다운로드

소개

NLP 모델은 종종 다른 문자 집합으로 다른 언어를 처리합니다. 유니 코드는 거의 모든 언어의 문자를 나타 내기 위해 사용되는 표준 인코딩 시스템입니다. 모든 유니 코드 문자는 고유 한 정수 사용하여 인코딩 된 코드 포인트 사이의 00x10FFFF . 유니 코드 문자열은 0 개 이상의 코드 포인트의 시퀀스입니다.

이 튜토리얼은 TensorFlow에서 유니코드 문자열을 표현하고 표준 문자열 연산과 동등한 유니코드를 사용하여 조작하는 방법을 보여줍니다. 스크립트 감지를 기반으로 유니코드 문자열을 토큰으로 분리합니다.

import tensorflow as tf
import numpy as np

tf.string 데이터 유형

기본 TensorFlow의 tf.string dtype 당신이 바이트 문자열의 텐서를 구축 할 수 있습니다. 유니 코드 문자열은 UTF-8 기본적으로 인코딩.

tf.constant(u"Thanks 😊")
2021-08-11 18:50:03.051591: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.060323: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.061297: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.062904: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-08-11 18:50:03.063621: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.064696: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.065596: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.699747: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.700774: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.701714: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.702652: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1510] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 14648 MB memory:  -> device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0
<tf.Tensor: shape=(), dtype=string, numpy=b'Thanks \xf0\x9f\x98\x8a'>

tf.string 텐서 취급 원자 단위로서 바이트 스트링. 이를 통해 다양한 길이의 바이트 문자열을 저장할 수 있습니다. 문자열 길이는 텐서 차원에 포함되지 않습니다.

tf.constant([u"You're", u"welcome!"]).shape
TensorShape([2])

당신이 구조 문자열에 파이썬을 사용하는 경우 참고 문자열 리터럴이 있습니다 기본적으로 유니 코드로 인코딩.

유니코드 표현

TensorFlow에서 유니코드 문자열을 나타내는 두 가지 표준 방법이 있습니다.

  • string 스칼라 - 코드 포인트의 시퀀스가 알려진하여 인코딩되는 문자 인코딩 .
  • int32 벡터 - 각각의 위치는 하나의 코드 포인트가 포함되어 있습니다.

예를 들어, 다음과 같은 세 가지의 값은 모든 유니 코드 문자열을 나타내는 "语言处理" (중국어하는 수단 "언어 처리") :

# Unicode string, represented as a UTF-8 encoded string scalar.
text_utf8 = tf.constant(u"语言处理")
text_utf8
<tf.Tensor: shape=(), dtype=string, numpy=b'\xe8\xaf\xad\xe8\xa8\x80\xe5\xa4\x84\xe7\x90\x86'>
# Unicode string, represented as a UTF-16-BE encoded string scalar.
text_utf16be = tf.constant(u"语言处理".encode("UTF-16-BE"))
text_utf16be
<tf.Tensor: shape=(), dtype=string, numpy=b'\x8b\xed\x8a\x00Y\x04t\x06'>
# Unicode string, represented as a vector of Unicode code points.
text_chars = tf.constant([ord(char) for char in u"语言处理"])
text_chars
<tf.Tensor: shape=(4,), dtype=int32, numpy=array([35821, 35328, 22788, 29702], dtype=int32)>

표현 간 변환

TensorFlow는 다음과 같은 서로 다른 표현 간에 변환하는 작업을 제공합니다.

tf.strings.unicode_decode(text_utf8,
                          input_encoding='UTF-8')
<tf.Tensor: shape=(4,), dtype=int32, numpy=array([35821, 35328, 22788, 29702], dtype=int32)>
tf.strings.unicode_encode(text_chars,
                          output_encoding='UTF-8')
<tf.Tensor: shape=(), dtype=string, numpy=b'\xe8\xaf\xad\xe8\xa8\x80\xe5\xa4\x84\xe7\x90\x86'>
tf.strings.unicode_transcode(text_utf8,
                             input_encoding='UTF8',
                             output_encoding='UTF-16-BE')
<tf.Tensor: shape=(), dtype=string, numpy=b'\x8b\xed\x8a\x00Y\x04t\x06'>

배치 차원

여러 문자열을 디코딩할 때 각 문자열의 문자 수가 같지 않을 수 있습니다. 반환 결과는이다 tf.RaggedTensor 가장 안쪽 치수 길이는 각 문자열의 문자 수에 따라 달라집니다.

# A batch of Unicode strings, each represented as a UTF8-encoded string.
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)
[104, 195, 108, 108, 111]
[87, 104, 97, 116, 32, 105, 115, 32, 116, 104, 101, 32, 119, 101, 97, 116, 104, 101, 114, 32, 116, 111, 109, 111, 114, 114, 111, 119]
[71, 246, 246, 100, 110, 105, 103, 104, 116]
[128522]

당신이 사용할 수있는 tf.RaggedTensor 직접, 또는 밀도로 변환 tf.Tensor 패딩이나와 tf.SparseTensor 방법 사용 tf.RaggedTensor.to_tensortf.RaggedTensor.to_sparse .

batch_chars_padded = batch_chars_ragged.to_tensor(default_value=-1)
print(batch_chars_padded.numpy())
[[   104    195    108    108    111     -1     -1     -1     -1     -1
      -1     -1     -1     -1     -1     -1     -1     -1     -1     -1
      -1     -1     -1     -1     -1     -1     -1     -1]
 [    87    104     97    116     32    105    115     32    116    104
     101     32    119    101     97    116    104    101    114     32
     116    111    109    111    114    114    111    119]
 [    71    246    246    100    110    105    103    104    116     -1
      -1     -1     -1     -1     -1     -1     -1     -1     -1     -1
      -1     -1     -1     -1     -1     -1     -1     -1]
 [128522     -1     -1     -1     -1     -1     -1     -1     -1     -1
      -1     -1     -1     -1     -1     -1     -1     -1     -1     -1
      -1     -1     -1     -1     -1     -1     -1     -1]]
batch_chars_sparse = batch_chars_ragged.to_sparse()

nrows, ncols = batch_chars_sparse.dense_shape.numpy()
elements = [['_' for i in range(ncols)] for j in range(nrows)]
for (row, col), value in zip(batch_chars_sparse.indices.numpy(), batch_chars_sparse.values.numpy()):
  elements[row][col] = str(value)
# max_width = max(len(value) for row in elements for value in row)
value_lengths = []
for row in elements:
  for value in row:
    value_lengths.append(len(value))
max_width = max(value_lengths)
print('[%s]' % '\n '.join(
    '[%s]' % ', '.join(value.rjust(max_width) for value in row)
    for row in elements))
[[   104,    195,    108,    108,    111,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _]
 [    87,    104,     97,    116,     32,    105,    115,     32,    116,    104,    101,     32,    119,    101,     97,    116,    104,    101,    114,     32,    116,    111,    109,    111,    114,    114,    111,    119]
 [    71,    246,    246,    100,    110,    105,    103,    104,    116,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _]
 [128522,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _]]

동일한 길이를 갖는 복수의 문자열을 인코딩 할 때, 사용 tf.Tensor 입력한다.

tf.strings.unicode_encode([[99, 97, 116], [100, 111, 103], [99, 111, 119]],
                          output_encoding='UTF-8')
<tf.Tensor: shape=(3,), dtype=string, numpy=array([b'cat', b'dog', b'cow'], dtype=object)>

다양한 길이와 여러 문자열을 인코딩 할 때, 사용 tf.RaggedTensor 입력한다.

tf.strings.unicode_encode(batch_chars_ragged, output_encoding='UTF-8')
<tf.Tensor: shape=(4,), dtype=string, numpy=
array([b'h\xc3\x83llo', b'What is the weather tomorrow',
       b'G\xc3\xb6\xc3\xb6dnight', b'\xf0\x9f\x98\x8a'], dtype=object)>

당신은 패딩이나 스파 스 형식으로 여러 문자열 텐서이 경우에 먼저 변환 tf.RaggedTensor 호출하기 전에 tf.strings.unicode_encode .

tf.strings.unicode_encode(
    tf.RaggedTensor.from_sparse(batch_chars_sparse),
    output_encoding='UTF-8')
<tf.Tensor: shape=(4,), dtype=string, numpy=
array([b'h\xc3\x83llo', b'What is the weather tomorrow',
       b'G\xc3\xb6\xc3\xb6dnight', b'\xf0\x9f\x98\x8a'], dtype=object)>
tf.strings.unicode_encode(
    tf.RaggedTensor.from_tensor(batch_chars_padded, padding=-1),
    output_encoding='UTF-8')
<tf.Tensor: shape=(4,), dtype=string, numpy=
array([b'h\xc3\x83llo', b'What is the weather tomorrow',
       b'G\xc3\xb6\xc3\xb6dnight', b'\xf0\x9f\x98\x8a'], dtype=object)>

유니코드 연산

문자 길이

사용 unit 의 매개 변수 tf.strings.length 문자 길이를 계산하는 방법을 나타 내기 위해 작전을. unit 디폴트 "BYTE" 하지만이 같은 다른 값으로 설정 될 수있다 "UTF8_CHAR" 또는 "UTF16_CHAR" 각 인코딩 된 문자열에서 유니 코드 코드 포인트의 수를 결정한다.

# Note that the final character takes up 4 bytes in UTF8.
thanks = u'Thanks 😊'.encode('UTF-8')
num_bytes = tf.strings.length(thanks).numpy()
num_chars = tf.strings.length(thanks, unit='UTF8_CHAR').numpy()
print('{} bytes; {} UTF-8 characters'.format(num_bytes, num_chars))
11 bytes; 8 UTF-8 characters

문자 부분 문자열

tf.strings.substr 영업 이익은 수용 unit 매개 변수를 친절 오프셋의 결정하는 데 사용하는 poslen paremeters가 포함되어 있습니다.

# Here, unit='BYTE' (default). Returns a single byte with len=1
tf.strings.substr(thanks, pos=7, len=1).numpy()
b'\xf0'
# Specifying unit='UTF8_CHAR', returns a single 4 byte character in this case
print(tf.strings.substr(thanks, pos=7, len=1, unit='UTF8_CHAR').numpy())
b'\xf0\x9f\x98\x8a'

유니코드 문자열 분할

tf.strings.unicode_split 작업은 개별 문자의 문자열로 유니 코드 문자열을 분할합니다.

tf.strings.unicode_split(thanks, 'UTF-8').numpy()
array([b'T', b'h', b'a', b'n', b'k', b's', b' ', b'\xf0\x9f\x98\x8a'],
      dtype=object)

문자의 바이트 오프셋

에 의해 생성 된 문자 텐서 정렬하려면 tf.strings.unicode_decode 원래의 문자열을, 그것은 각 문자가 시작되는 오프셋을 알고 유용합니다. 메소드 tf.strings.unicode_decode_with_offsets 비슷 unicode_decode 는 각 문자의 개시 오프셋을 포함하는 제 텐서를 반환하는 것을 제외.

codepoints, offsets = tf.strings.unicode_decode_with_offsets(u'🎈🎉🎊', 'UTF-8')

for (codepoint, offset) in zip(codepoints.numpy(), offsets.numpy()):
  print('At byte offset {}: codepoint {}'.format(offset, codepoint))
At byte offset 0: codepoint 127880
At byte offset 4: codepoint 127881
At byte offset 8: codepoint 127882

유니코드 스크립트

각 유니 코드 코드 포인트는로 알려진 코드 포인트의 단일 컬렉션에 속한 스크립트 . 문자의 스크립트는 문자가 사용되는 언어를 결정하는 데 도움이 됩니다. 예를 들어 'Б'가 키릴 문자라는 사실을 아는 것은 해당 문자를 포함하는 현대 텍스트가 러시아어나 우크라이나어와 같은 슬라브어에서 유래했을 가능성이 있음을 나타냅니다.

TensorFlow는 제공 tf.strings.unicode_script 하는 스크립트 주어진 코드 포인트의 사용을 결정하는 작업을. 스크립트 코드는 int32 에 해당하는 값 유니 코드에 대한 국제 구성 요소 (ICU) UScriptCode 값.

uscript = tf.strings.unicode_script([33464, 1041])  # ['芸', 'Б']

print(uscript.numpy())  # [17, 8] == [USCRIPT_HAN, USCRIPT_CYRILLIC]
[17  8]

tf.strings.unicode_script 동작은 다차원에 적용될 수 tf.Tensor S 또는 tf.RaggedTensor S의 코드 점 :

print(tf.strings.unicode_script(batch_chars_ragged))
<tf.RaggedTensor [[25, 25, 25, 25, 25], [25, 25, 25, 25, 0, 25, 25, 0, 25, 25, 25, 0, 25, 25, 25, 25, 25, 25, 25, 0, 25, 25, 25, 25, 25, 25, 25, 25], [25, 25, 25, 25, 25, 25, 25, 25, 25], [0]]>

예: 단순 세분화

분할은 텍스트를 단어와 같은 단위로 분할하는 작업입니다. 공백 문자를 사용하여 단어를 구분하는 경우 이는 종종 쉽지만 일부 언어(예: 중국어 및 일본어)에서는 공백을 사용하지 않고 일부 언어(예: 독일어)에는 의미를 분석하기 위해 분할해야 하는 긴 합성어가 포함되어 있습니다. 웹 텍스트에서 "NY株価"(뉴욕 증권 거래소)와 같이 다른 언어와 스크립트가 함께 혼합되는 경우가 많습니다.

스크립트의 변경 사항을 사용하여 단어 경계를 근사화함으로써 매우 거친 분할(ML 모델을 구현하지 않고)을 수행할 수 있습니다. 위의 "NY株価" 예와 같은 문자열에 대해 작동합니다. 다양한 스크립트의 공백 문자는 모두 실제 텍스트와 다른 특수 스크립트 코드인 USCRIPT_COMMON으로 분류되기 때문에 공백을 사용하는 대부분의 언어에서도 작동합니다.

# dtype: string; shape: [num_sentences]
#
# The sentences to process.  Edit this line to try out different inputs!
sentence_texts = [u'Hello, world.', u'世界こんにちは']

먼저 문장을 문자 코드 포인트로 디코딩하고 각 문자에 대한 스크립트 식별자를 찾습니다.

# dtype: int32; shape: [num_sentences, (num_chars_per_sentence)]
#
# sentence_char_codepoint[i, j] is the codepoint for the j'th character in
# the i'th sentence.
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_scripts[i, j] is the Unicode script of the j'th character in
# the i'th sentence.
sentence_char_script = tf.strings.unicode_script(sentence_char_codepoint)
print(sentence_char_script)
<tf.RaggedTensor [[72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 46], [19990, 30028, 12371, 12435, 12395, 12385, 12399]]>
<tf.RaggedTensor [[25, 25, 25, 25, 25, 0, 0, 25, 25, 25, 25, 25, 0], [17, 17, 20, 20, 20, 20, 20]]>

스크립트 식별자를 사용하여 단어 경계를 추가해야 하는 위치를 결정합니다. 각 문장의 시작과 스크립트가 이전 문자와 다른 각 문자에 대해 단어 경계를 추가합니다.

# dtype: bool; shape: [num_sentences, (num_chars_per_sentence)]
#
# sentence_char_starts_word[i, j] is True if the j'th character in the i'th
# sentence is the start of a word.
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] is the index of the character that starts the i'th word (in
# the flattened list of characters from all sentences).
word_starts = tf.squeeze(tf.where(sentence_char_starts_word.values), axis=1)
print(word_starts)
tf.Tensor([ 0  5  7 12 13 15], shape=(6,), dtype=int64)

그런 다음 구축하는 시작 오프셋을 사용할 수 있습니다 RaggedTensor 모든 배치에서 단어의 목록을 포함합니다.

# dtype: int32; shape: [num_words, (num_chars_per_word)]
#
# word_char_codepoint[i, j] is the codepoint for the j'th character in the
# i'th word.
word_char_codepoint = tf.RaggedTensor.from_row_starts(
    values=sentence_char_codepoint.values,
    row_starts=word_starts)
print(word_char_codepoint)
<tf.RaggedTensor [[72, 101, 108, 108, 111], [44, 32], [119, 111, 114, 108, 100], [46], [19990, 30028], [12371, 12435, 12395, 12385, 12399]]>

마무리에, 세그먼트 단어는 코드 포인트 RaggedTensor 가독성을 위해 UTF-8 문자열로 문장 및 인코딩에 다시.

# dtype: int64; shape: [num_sentences]
#
# sentence_num_words[i] is the number of words in the i'th sentence.
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] is the codepoint for the k'th character
# in the j'th word in the i'th sentence.
sentence_word_char_codepoint = tf.RaggedTensor.from_row_lengths(
    values=word_char_codepoint,
    row_lengths=sentence_num_words)
print(sentence_word_char_codepoint)

tf.strings.unicode_encode(sentence_word_char_codepoint, 'UTF-8').to_list()
<tf.RaggedTensor [[[72, 101, 108, 108, 111], [44, 32], [119, 111, 114, 108, 100], [46]], [[19990, 30028], [12371, 12435, 12395, 12385, 12399]]]>
[[b'Hello', b', ', b'world', b'.'],
 [b'\xe4\xb8\x96\xe7\x95\x8c',
  b'\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf']]