这篇内容是在学习VGG16时,使用该模型进行Fine-tuning的时候,需要加载模型已有的权重,但是,在使用model.load_weights函数加载权重时,会报错AttributeError: 'str' object has no attribute 'decode'

经过人类体外大脑的Google老师检索后发现,这似乎是当前版本(2.4.0-rc0)中内建的keras的一个bug,更新为最新版本的tf/keras即可解决这个问题。

于是看到这个的我非常放心的来到了当初安装这个tf版本的Apple的github仓库tensorflow_macos

发现了readme中写着这样的文字:

You can now leverage Apple’s tensorflow-metal PluggableDevice in TensorFlow v2.5 for accelerated training on Mac GPUs directly with Metal. Learn more here.

跟着链接点进去之后发现,现在的tf2.5和2.6已经正式支持了,并且安装方式变得简单多了。

虽然当时就有看到OS Requirements中写着的macOS 12.0+(latest beta)的字样,但是以我多年的经验(现在想想我想锤死我自己),判断就算我现在的11.2.3的系统也是可以正常使用的。

于是就跟着页面上的安装步骤一步一步地进行了安装,安装进行的非常快,似乎一切都是那么正常。

安装完成后,我使用了之前出错的代码进行了测试,发现代码正常运行了!

于是我就继续愉快的敲代码下去了。

当我敲下最后一行代码,优雅的按下执行快捷键之后,风云突变,这次问题来到了训练用的代码部分(也就是model.fit),这个时候报错了一大~堆内容。

经过检索后来到了apple的developer社区,结果发现了解决方法,问题还是出在了系统版本上,需要升级到最新系统。

那么问题就陷入了无法解决的困境。

  1. 因为侧载应用的关系,我不想升级电脑的系统
  2. 因为无法升级系统的关系,我无法升级到最新的TensorFlow
  3. 因为需要使用ML加速,我无法单独使用keras

在仔细阅读了报错的内容之后,我发现报错的部分似乎指向了路径~//Users/usrname/miniforge3/envs/TensorFlow/lib/python3.8/site-packages/tensorflow/python/keras/saving/hdf5_format.py这个文件

这时候我突发奇想,如果修改一下源代码会怎么样,虽然能力不是很高,但是看了一下源代码,再看了一下报错,发现其实只要在这里加一个判断语句而不是上来直接进行编码转换的话应该就能够解决问题。

但是这个时候我又一想,高版本的这个既然是已经修复了bug的话,那我为何不去看一看高版本的这个文件是怎样修改的呢

说干就干,拎出来两个文件,使用vsc进行diff的对比,然后找到decode的部分进行一一修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  if 'keras_version' in f.attrs:
- original_keras_version = f.attrs['keras_version'].decode('utf8')
+ original_keras_version = f.attrs['keras_version']
+ if hasattr(original_keras_version, 'decode'):
+ original_keras_version = original_keras_version.decode('utf8')
else:
original_keras_version = '1'
if 'backend' in f.attrs:
- original_backend = f.attrs['backend'].decode('utf8')
+ original_backend = f.attrs['backend']
+ if hasattr(original_backend, 'decode'):
+ original_backend = original_backend.decode('utf8')
else:
original_backend = None
···
···
···
···
Raises:
ValueError: in case of mismatch between provided layers
and weights file and skip_match=False.
"""
if 'keras_version' in f.attrs:
- original_keras_version = f.attrs['keras_version'].decode('utf8')
+ original_keras_version = f.attrs['keras_version']
+ if hasattr(original_keras_version, 'decode'):
+ original_keras_version = original_keras_version.decode('utf8')
else:
original_keras_version = '1'
if 'backend' in f.attrs:
- original_backend = f.attrs['backend'].decode('utf8')
+ original_backend = f.attrs['backend']
+ if hasattr(original_backend, 'decode'):
+ original_backend = original_backend.decode('utf8')
else:
original_backend = None

以及

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def load_attributes_from_hdf5_group(group, name):
"""Loads attributes of the specified name from the HDF5 group.

This method deals with an inherent problem
of HDF5 file which is not able to store
data larger than HDF5_OBJECT_HEADER_LIMIT bytes.

Arguments:
group: A pointer to a HDF5 group.
name: A name of the attributes to load.

Returns:
data: Attributes data.
"""
if name in group.attrs:
- data = [n.decode('utf8') for n in group.attrs[name]]
+ data = [
+ n.decode('utf8') if hasattr(n, 'decode') else n
+ for n in group.attrs[name]
+ ]
else:
data = []
chunk_id = 0
while '%s%d' % (name, chunk_id) in group.attrs:
- data.extend(
- [n.decode('utf8') for n in group.attrs['%s%d' % (name, chunk_id)]])
+ data.extend([
+ n.decode('utf8') if hasattr(n, 'decode') else n
+ for n in group.attrs['%s%d' % (name, chunk_id)]
+ ])
chunk_id += 1
return data

修改好上述的内容之后,成功解决了所有的问题

终于,我又水完了一篇内容(笑)