본문 바로가기

ValueError: output array is read-only

mnist = tf.keras.datasets.fashion_mnist
(training_images, training_labels), (test_images, test_labels) = mnist.load_data()
training_images /= 255.0
test_images /= 255.0
model = tf.keras.models.Sequential([
                                    tf.keras.layers.Flatten(),
                                    tf.keras.layers.Dense(128, activation=tf.nn.relu),
                                    tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
model.fit(training_images, training_labels, epochs=5)

test_loss = model.evaluate(test_images, test_labels)

코드 출처: C1_W3_Lab_1_improving_accuracy_using_convolutions.ipynb

 

텐서플로우 자격증을 준비하기 위해 코세라 강의를 듣고 실습하던 중 다음과 같은 오류가 떴다. 

 

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-5-a45d78a6ac67> in <module>()
      1 mnist = tf.keras.datasets.fashion_mnist
      2 (training_images, training_labels), (test_images, test_labels) = mnist.load_data()
----> 3 training_images /= 255.0
      4 test_images /= 255.0
      5 model = tf.keras.models.Sequential([

ValueError: output array is read-only

 

training_images = training_images / 255.0랑 training_images /= 255.0는 분명 같은 거라고 배웠는데 왜 오류가 날까? 에러 내용을 구글링하니 나와 같은 고민을 하는 사람을 바로 찾을 수 있었다.

 

Why does `/=` raise an error but not `x = x / y` with a read-only numpy array?

I've always thought x /= y is equal to x = x / y. But now I'm facing a situation that I'll have an error when I use /= but not when using x = x / y. so definitely they shouldn't be the same in pyth...

stackoverflow.com

 

읽기 귀찮은 사람들을 위해 간단히 말하자면 numpy array는 read-only다. 그 말은 즉슨 immutable한, 변경할 수 없는 객체라는 소리다. 그런데 /= 연산은 in-place연산이기 때문에 무조건 mutable한 객체에 대해서만 동작한다. 따라서 immutable한 객체는 아예 새로 할당을 해줘야 한다. x /= y가 아닌 x = x / y로 말이다.

결론: numpy array는 할당 연산자 사용 시 풀어서 써줘야 한다!

엥 그런데 에러가 뜨지 않고 정상적으로 잘 작동하는 경우도 있다.

 

import numpy as np
import random
from tensorflow.keras.preprocessing.image import img_to_array, load_img

# Let's define a new Model that will take an image as input, and will output
# intermediate representations for all layers in the previous model after
# the first.
successive_outputs = [layer.output for layer in model.layers[1:]]
#visualization_model = Model(img_input, successive_outputs)
visualization_model = tf.keras.models.Model(inputs = model.input, outputs = successive_outputs)
# Let's prepare a random input image from the training set.
horse_img_files = [os.path.join(train_horse_dir, f) for f in train_horse_names]
human_img_files = [os.path.join(train_human_dir, f) for f in train_human_names]
img_path = random.choice(horse_img_files + human_img_files)

img = load_img(img_path, target_size=(300, 300))  # this is a PIL image
x = img_to_array(img)  # Numpy array with shape (150, 150, 3)
x = x.reshape((1,) + x.shape)  # Numpy array with shape (1, 150, 150, 3)

# Rescale by 1/255
x /= 255

# Let's run our image through our network, thus obtaining all
# intermediate representations for this image.
successive_feature_maps = visualization_model.predict(x)

# These are the names of the layers, so can have them as part of our plot
layer_names = [layer.name for layer in model.layers[1:]]

# Now let's display our representations
for layer_name, feature_map in zip(layer_names, successive_feature_maps):
  if len(feature_map.shape) == 4:
    # Just do this for the conv / maxpool layers, not the fully-connected layers
    n_features = feature_map.shape[-1]  # number of features in feature map
    # The feature map has shape (1, size, size, n_features)
    size = feature_map.shape[1]
    # We will tile our images in this matrix
    display_grid = np.zeros((size, size * n_features))
    for i in range(n_features):
      # Postprocess the feature to make it visually palatable
      x = feature_map[0, :, :, i]
      x -= x.mean()
      x /= x.std()
      x *= 64
      x += 128
      x = np.clip(x, 0, 255).astype('uint8')
      # We'll tile each filter into this big horizontal grid
      display_grid[:, i * size : (i + 1) * size] = x
    # Display the grid
    scale = 20. / n_features
    plt.figure(figsize=(scale * n_features, scale))
    plt.title(layer_name)
    plt.grid(False)
    plt.imshow(display_grid, aspect='auto', cmap='viridis')

코드 출처: C1_W4_Lab_1_image_generator_no_validation.ipynb

 

코드 다 볼 필요 없고 21번째 줄을 보면 x가 numpy array인데 코드 실행 시 전혀 문제가 생기지 않았다. 뭐지.....? 42, 43, 44, 45번째 줄도 마찬가지다.

결론: numpy array에서 할당 연산자 사용이 안되면 풀어서 써주자!