CelebA データがtensorflow_datasetsから読み込めないので画像をローカルに落として取り込む

#1. はじめに

ブログ初投稿させていただきます。
ブログ投稿を通じて、自分自身も読んでいただいた方も何かプラス効果があることを期待しています。

私は現在機械学習について勉強中なのですが、その中でわからなかったことや調べて理解したつもりになっていることを投稿していきたいと思っています。

環境
OS: Windows10
TensorFlow: 2.6.0
Python: 3.9.5
#2. tensorflow_datasetsからCelebAのデータが読み込めない

CelebAデータを用いて、画像認識を試してみようという例は参考書やネットにいくつもあると思います。
どの例もまず、画像を取り込むことから始まると思うのですが、ここで苦戦しました。
私が読んでいる参考書では、

```python
import tensorflow as tf
import tensorflow_datasets as tfds

celeba_bldr = tfds.builder('celeb_a')
celeba_bldr.download_and_prepare()
```

でデータを読み込んで話を進めていますが、
これを実行してもエラーが出てしまいました。

```
エラー
tensorflow_datasets.core.download.download_manager.NonMatchingChecksumError: 
Artifact https://drive.google.com/uc?export=download&id=0B7EVK8r0v71pZjFTYXZWM3FlRnM, 
downloaded to C:\Users\USER_NAME\tensorflow_datasets\downloads\ucexport_download_id
_0B7EVK8r0v71pZjFTYXZWM3FlDDaXUAQO8EGH_a7VqGNLRtW52mva1LzDrbV723OQN8.tmp.
e75b4118edb24ae2950b41837362c311\ServiceLogin,
has wrong checksum:
```
これはホスト側の問題でユーザー側ではどうにもならない様子。

それでは、画像を実際にローカルに落として読み込めばできるのではと思い、
色々と調べながらやってみました。

この方法は自分で機械画像を用意して学習を行いたい場合なんかにも、
応用できるのではないかと思っています。

#3. 方法
##3-1. CelebAデータのダウンロード

まず画像のダウンロードですが、こちらの記事を参考にさせていただきました。

http://cedro3.com/ai/celeba-dataset-attribute/

http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html

実際には、GoogleDrive上の
Colab Notebooks > GAN > CelebA > img > img_align_celeba.zip
Colab Notebooks > GAN > CelebA > Anno > list_attr_celeba.txt
の2つをダウンロードしました。

##3-2. 画像リストの読み込み

画像ファイルは以下のパスに入れたので、そのフォルダ内で全jpgファイルのパスをリスト化します。
/img_align_celeba/000001.jpg

```python
import pathlib
imgdir_path = pathlib.Path('img_align_celeba')
file_list = sorted([str(path) for path in imgdir_path.glob('*.jpg')])
```

これでfile_listは各画像ファイルのパスを集めたものになります。

```python
print(file_list[:5])

結果
['img_align_celeba\\000001.jpg', 'img_align_celeba\\000002.jpg', 'img_align_celeba\\000003.jpg', 'img_align_celeba\\000004.jpg', 'img_align_celeba\\000005.jpg']
```

##3-3. ラベルの読み込み

ラベルファイルの中身は以下の通りになっています。

![アトリビュートファイル_中身.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2048607/8139bd68-0241-23e9-61bf-873747ecf099.png)

実際の特徴量は2行目からなので、2行目からデータを読み取ります。
また、1列目に画像ファイル名がありますが、これは3-2で読み込んだので飛ばします。
2列目以降の特徴量ですが、このファイルではTrue `1`, False `-1`となっています。
False `-1`でもtanh()を使えば行けるのかもしれませんが、最終的に所属確率が出しやすいかと思い、
False `0`に変換しました。(関係ない？)
また、readlinesではすべてstr形式で読み込みますので、後の学習に用いることを考えてint形式に変換しています。

```python
i=0
label_list = []
with open("img_align_celeba/list_attr_celeba.txt","r") as f:
    lines = f.readlines()
    for line in lines:
        if i > 1:
            line = line.split()
            #1列目のインデックスを除いてint化、-1を0に変換
            line = [int(i) if i == '1' else int(0) for i in line[1:]]
            label_list.append(line)
        i += 1
```

これで40個の特徴量を取得しました。

```python
print(label_list[:1])

結果
[[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1]]
```


##3-4. 画像パスとラベルの結合とDataset化

以下のコードで画像パスとラベルを結合しDataset形式にします。

```python
import tensorflow as tf
ds_files_labels = tf.data.Dataset.from_tensor_slices((file_list,label_list))
```

画像はパスからデータを取得する関数(load_and_preprocess)を別途作成し、
map関数ですべてデータに適用します。

```python
def load_and_preprocess(path, label):
    """画像パスから画像を読み込み、ラベルと共に返す関数"""
    image = tf.io.read_file(path)
    image = tf.image.decode_jpeg(image, channels=3)
    image = tf.cast(image,tf.float32) / 255.0
    return image,label

ds_images_labels = ds_files_labels.map(load_and_preprocess)
```
これでデータの取り込みは完了です。

##3-5. 結果の確認

matplotlibを使ってデータの可視化を行います。
性別は21個目の特徴量で、1でMale、0でFemaleを示すので、
それを各画像に対応して表示させます。

```python
import matplotlib.pyplot as plt

fig = plt.figure(figsize = (10,5))
for i,example in enumerate(ds_images_labels.take(6)):
    ax = fig.add_subplot(2,3,i+1)
    ax.set_xticks([]);ax.set_yticks([])
    ax.imshow(example[0])
    if example[1][20].numpy() == 1:
        label='Male'
    else:
        label='Female'
    ax.set_title('{}'.format(label),size=15)

plt.show()

```
結果
![Figure_34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2048607/7ac411ea-3c7f-7fab-2fcb-68ae5b8d3ba5.png)

正しく読み込めていることが確認できます。


#4. まとめ
改めてまとめてみるとシンプルですが、実際にやっていたときは何回も行き詰りました。

次回はこのデータを用いてCNNを試してみようかと思っています。