TensorFlow 之中的 Unicode 數據格式的處理
在我們之前的數據處理過程之中,我們都是采用的 ASCII 碼或者其他編碼處理數據格式的,但是這些編碼并不能夠完全表示當前所有語言的所有字符,比如我們就無法使用 ASCII 碼來表示漢語。因此這個時候我們就需要用到一種新的編碼方式來進行字符的處理,于是這節課我們來學習如何在 TensorFlow 之中處理 Unicode 格式的數據。
1. 在 TensorFlow之中創建 Unicode 字符串以及張量
在 TensorFlow 之中,Unicode是存儲在 tf.string 數據類型之中的,而在默認的情況之下,Unicode在 TensorFlow 之中的默認的編碼格式是 UTF-8 編碼的,我們可以通過以下示例查看具體的細節:
ch_string = u"你好呀!"
en_string = u"Hello"
ch_string_utf_8 = tf.constant(ch_string)
en_string_utf_8 = tf.constant(en_string)
print(ch_string_utf_8, en_string_utf_8, sep='\n')
在這段代碼之中,我們著重進行了以下的操作:
- 在字符串前加上了 u,從而指示該字符串為 Unicode 格式;
- 我們使用 tf.constant 函數來將字符串轉化為 Tensor 張量。
我們可以得到如下輸出:
tf.Tensor(b'\xe4\xbd\xa0\xe5\xa5\xbd\xe5\x91\x80\xef\xbc\x81', shape=(), dtype=string)
tf.Tensor(b'Hello', shape=(), dtype=string)
我們可以發現以下幾點:
- 這兩個 Tensor 的數據類型都為 string ,這其實是 TensorFlow 內部的 tf.string 數據類型;
- 這兩 個Tensor 的 Shape 都為空,因為在 TensoFlow 之中不會為 Unicode 字符串賦予形狀,這是因為字符串的長度不盡相同;
- 第一個中文的字符串被按照 UTF-8 規則進行了編碼,而英文并沒有進行編碼(嚴格來說,英文也進行了編碼,只是編碼前后相同,這一點可以由字符串前面的b就可以看出)。
2. TensorFlow 之中 Unicode 字符串的存在形式
在TensorFlow之 中, Unicode 字符串有兩種表現形式,它們分別是:
- 編碼格式:使用編碼規則進行編碼后的字符串,比如 UTF-8、UTF-16 等編碼方式;
- 解碼格式:對于每一個字符按照唯一的整數進行編碼之后的格式,這些整數被稱作“代碼點”。
在第一小節之中我們看到的形式就是編碼格式,而且編碼方式為 UTF-8,對于兩種格式,我們可以通過 tf.strings.unicode_decode 以及 tf.strings.unicode_encode 進行相應的轉化,比如以下示例:
ch_string_utf_8_decode = tf.strings.unicode_decode(ch_string_utf_8, input_encoding='UTF-8')
ch_string_utf_8_encode = tf.strings.unicode_encode(ch_string_utf_8_decode, output_encoding='UTF-8')
print(ch_string_utf_8_decode)
print(ch_string_utf_8_encode)
在這 tf.strings.unicode_decode 函數之中,包含兩個參數:
- 第一個參數就是我們要進行解碼的字符串,比如我們的 ch_string_utf_8 ;
- 第二個參數是輸入字符串的編碼格式,因為我們的字符串編碼格式為 UTF-8 ,因此在這里我們的參數為input_encoding=‘UTF-8’。
tf.strings.unicode_encode 函數與 tf.strings.unicode_decode 函數相似,只是第二個參數是輸出字符串的編碼方式,因為我們需要 UTF-8 編碼的格式,因此這里我們選擇 output_encoding=‘UTF-8’。
我們可以得到輸出:
tf.Tensor([20320 22909 21568 65281], shape=(4,), dtype=int32)
tf.Tensor(b'\xe4\xbd\xa0\xe5\xa5\xbd\xe5\x91\x80\xef\xbc\x81', shape=(), dtype=string)
我們發現解碼后的字符串就是一串整數數組,其中的每個整數代表著一個中文字符;于此同時,更重要的是解碼產生的數組是擁有形狀的,而正因如此,解碼后的表示更加適合我們用作數據集。
同時我們也可以發現 ch_string_utf_8_encode 與 ch_string_utf_8 兩個完全一樣,因為 ch_string_utf_8 本來就是編碼的字符串嘛。
3. 單個 Unicode 字符串的處理
無論 Unicode 格式怎么編碼,Unicode 字符串終歸是字符串,因此在實際應用之中就會進行各種的字符串操作,因此我們有必要來學習一下在 TensorFlow 之中的 Unicode 字符串的基本處理操作。
3.1 如何獲取 Unicode 字符串的長度
我們可以使用 tf.strings.length 函數來獲取 Unicode 字符串的長度,該函數含有兩個重要的參數:
- str,要獲取長度的字符串;
- unit,長度的單位,目前包含兩個選項,一個是“BYTE”,另一個是“UTF8_CHAR”:
- BYTE,按照字節進行計數,從而獲取字符串的長度;
- UTF8_CHAR,按照單個 Unicode 字符的單位進行計數,獲取我們通常認知的長度。
同時該 API 返回的是一個 Tensor ,我們可以通過 numpy() 函數來將其轉化為我們可以直接使用的數字長度。
比如以下代碼:
len_bytes = tf.strings.length(ch_string_utf_8, unit='BYTE')
len_chars = tf.strings.length(ch_string_utf_8, unit='UTF8_CHAR')
print(len_bytes, len_chars)
print(len_bytes.numpy(), len_chars.numpy())
我們可以得到如下輸出:
tf.Tensor(12, shape=(), dtype=int32) tf.Tensor(4, shape=(), dtype=int32)
12 4
可以看到,“你好呀!”字符串含有 12 個字節長度,而且正如我們看到的那樣,包含 4 個漢字字符。
3.2 子字符串的操作
對于 Unicode 子字符串的操作,我們可以通過 tf.strings.substr 函數來實現,該 API 接收 4 個參數,它們分別是:
- str,要進行子字符串操作的 Unicode 字符串;
- unit,與前面的 unit 一樣,表示截取的單位,包含“BYTE”以及“UTF8_CHAR”兩個選項;
- pos,開始截取的位置;
- len,截取的長度。
我們可以通過以下示例進行查看:
print(tf.strings.substr(ch_string_utf_8, pos=3, len=1, unit='BYTE'))
print(tf.strings.substr(ch_string_utf_8, pos=3, len=1, unit='UTF8_CHAR'))
我們可以得到如下輸出:
tf.Tensor(b'\xe5', shape=(), dtype=string)
tf.Tensor(b'\xef\xbc\x81', shape=(), dtype=string)
我們可以發現,b’\xe5 剛剛好是 3 位置的字符串,而 b’\xef\xbc\x81’ 剛剛好是最后一個“!”的 Unicode 表示。
3.3 字符串的拆分
通過拆分操作,我們可以將每個Unicode字符進行拆分,從而形成一個數組,每個數組包含一個 Unicode 字符的編碼。
對于該操作,我們可以通過 tf.strings.unicode_split 函數實現,該函數的具體使用如下:
print(tf.strings.unicode_split(ch_string_utf_8, 'UTF-8'))
其中的第二個參數表示的是字符串的編碼方式,我們可以得到如下輸出:
tf.Tensor([b'\xe4\xbd\xa0' b'\xe5\xa5\xbd' b'\xe5\x91\x80' b'\xef\xbc\x81'], shape=(4,), dtype=string)
我們看到,我們的字符串已經成功進行了拆分的基本操作。
4. 使用 Unicode 數據構造數據集的示例
在實際的使用之中,我們大致分為以下幾步來構造 Unicode 字符串的數據集:
- 首先將 Unicode 字符串數據進行解碼,因為這樣就可以計算長度;
- 將其統一為定長的形式;
- 構造數據集
對于解碼,我們可以通過之前的 tf.strings.unicode_decode 函數進行解碼,我們可以通過下面的示例查看解碼的結果:
data_string = [u"你好呀", u"很高興認識你", u"Hello", u"Nice to meet you"]
decode_data = tf.strings.unicode_decode(data_string, input_encoding='UTF-8')
print(decode_data, decode_data.shape, sep='\n')
我們可以得到的輸出為:
<tf.RaggedTensor [[20320, 22909, 21568], [24456, 39640, 20852, 35748, 35782, 20320], [72, 101, 108, 108, 111], [78, 105, 99, 101, 32, 116, 111, 32, 109, 101, 101, 116, 32, 121, 111, 117]]>
(4, None)
可以發現,我們得到的數據為 tf.RaggedTensor 格式,而這種格式的每個元素都不是定長的,而這就到十六我們的數據的 shape 只能為(4, None),因此我們可以通過to_tensor()函數來將其轉化為定長的張量。
decode_data_pad = decode_data.to_tensor()
print(decode_data_pad, decode_data_pad.shape, sep='\n')
我們可以得到如下結果:
tf.Tensor(
[[20320 22909 21568 0 0 0 0 0 0 0 0 0
0 0 0 0]
[24456 39640 20852 35748 35782 20320 0 0 0 0 0 0
0 0 0 0]
[ 72 101 108 108 111 0 0 0 0 0 0 0
0 0 0 0]
[ 78 105 99 101 32 116 111 32 109 101 101 116
32 121 111 117]], shape=(4, 16), dtype=int32)
(4, 16)
由此我們可以發現,我們的數據已經 Padding 到了統一的長度,而這個長度是根據最長的字符串的長度來決定的。這樣之后,我們便可以進一步構造數據集,我們將會采用定長與不定長的數據分別構造數據集,來查看兩者的區別。
在這里我們可以使用虛擬的標簽進行操作, 我們依然使用傳統的 tf.data.Dataset.from_tensor_slices 函數來進行數據集的構建:
labels = [0, 0, 0, 0]
dataset = tf.data.Dataset.from_tensor_slices((decode_data, labels))
dataset_pad = tf.data.Dataset.from_tensor_slices((decode_data_pad, labels))
print(dataset)
print(dataset_pad)
我們可以得到結果:
<TensorSliceDataset shapes: ((None,), ()), types: (tf.int32, tf.int32)>
<TensorSliceDataset shapes: ((16,), ()), types: (tf.int32, tf.int32)>
我們可以看到,沒有采用 Padding 的數據集的形狀為 ((None,), ()) ,而采用了 Padding 數據集的形狀為((16,), ()),而后者是會對我們的使用有利的,因此我們推薦使用后者進行操作。
5. 小結
在這節課之中,我們學習了如何在 TensorFlow 之中使用 Unicode 字符串,我們同時學習了 Unicode 字符串的兩種存在形式,又了解了 Unicode 字符串的基本操作,最后我們通過一個簡單的示例了解了如何使用 Unicode 字符串構造數據集。