Transfer Learning Design Pattern

In Transfer Learning, we take part of a previously trained model, freeze the weights, and incorporate these non-trainable layers into a new model that solves a similar problem, but on a smaller dataset.


In [0]:
import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds
import tensorflow_hub as hub
import matplotlib.pyplot as plt

from tensorflow import keras
from tensorflow.keras import Sequential

Building a medical imaging classification model with Keras and VGG

We'll use a colorectal histology dataset that comes pre-installed with TF Datasets. First we'll extract the data as a TF Dataset. Then we'll load a VGG model without the top classification layers. Finally, we'll add layers specific to our classification problem so that the final model outputs a softmax classification corresponding with 1 of the 8 classes in our dataset.


In [0]:
# These images will be (150,150,3)
(train, validation, test), info = tfds.load(
    'colorectal_histology',
    split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'], 
    shuffle_files=True, 
    as_supervised=True,
    with_info=True
)

In [53]:
# Some info on the original dataset
info


Out[53]:
tfds.core.DatasetInfo(
    name='colorectal_histology',
    version=2.0.0,
    description='Classification of textures in colorectal cancer histology. Each example is a 150 x 150 x 3 RGB image of one of 8 classes.',
    homepage='https://zenodo.org/record/53169#.XGZemKwzbmG',
    features=FeaturesDict({
        'filename': Text(shape=(), dtype=tf.string),
        'image': Image(shape=(150, 150, 3), dtype=tf.uint8),
        'label': ClassLabel(shape=(), dtype=tf.int64, num_classes=8),
    }),
    total_num_examples=5000,
    splits={
        'train': 5000,
    },
    supervised_keys=('image', 'label'),
    citation="""@article{kather2016multi,
      title={Multi-class texture analysis in colorectal cancer histology},
      author={Kather, Jakob Nikolas and Weis, Cleo-Aron and Bianconi, Francesco and Melchers, Susanne M and Schad, Lothar R and Gaiser, Timo and Marx, Alexander and Z{"o}llner, Frank Gerrit},
      journal={Scientific reports},
      volume={6},
      pages={27988},
      year={2016},
      publisher={Nature Publishing Group}
    }""",
    redistribution_info=,
)

In [0]:
# Utility for what each label corresponds with
label_map = ['tumor','stroma','complex','lympho','debris','mucosa', 'adipose']

In [56]:
# Preview 2 examples from our dataset
get_label_name = info.features['label'].int2str
for image, label in train.take(2):
  plt.figure()
  plt.imshow(np.array(image))
  plt.title(get_label_name(label))


The labels in the original dataset are single scalar values (ranging from 0 to 7). We need to convert these to softmax arrays to train our model.


In [0]:
def label_format(image, label):
  return (image, tf.one_hot(label, depth=8))

train = train.map(label_format)
validation = validation.map(label_format)
test = test.map(label_format)

In [0]:
# Create batches
train_batch = train.shuffle(500).batch(32)
val_batch = validation.batch(32)
test_batch = test.batch(32)

In [71]:
for image_batch, label_batch in train_batch.take(1):
   pass

image_batch.shape


Out[71]:
TensorShape([32, 150, 150, 3])

In [0]:
# Load the VGG model and set trainable to false
vgg_model = tf.keras.applications.VGG19(
    include_top=False, 
    weights='imagenet', 
    input_shape=((150,150,3)), 
    classifier_activation='softmax'
)

In [0]:
vgg_model.trainable = False

In [74]:
vgg_model.summary()


Model: "vgg19"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_6 (InputLayer)         [(None, 150, 150, 3)]     0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 150, 150, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 150, 150, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 75, 75, 64)        0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 75, 75, 128)       73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 75, 75, 128)       147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 37, 37, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 37, 37, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 37, 37, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 37, 37, 256)       590080    
_________________________________________________________________
block3_conv4 (Conv2D)        (None, 37, 37, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 18, 18, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 18, 18, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 18, 18, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 18, 18, 512)       2359808   
_________________________________________________________________
block4_conv4 (Conv2D)        (None, 18, 18, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 9, 9, 512)         0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_conv4 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 4, 4, 512)         0         
=================================================================
Total params: 20,024,384
Trainable params: 0
Non-trainable params: 20,024,384
_________________________________________________________________

In [0]:
feature_batch = vgg_model(image_batch)

In [76]:
global_avg_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_avg = global_avg_layer(feature_batch)
print(feature_batch_avg.shape)


(32, 512)

In [77]:
prediction_layer = tf.keras.layers.Dense(8, activation='softmax')
prediction_batch = prediction_layer(feature_batch_avg)
print(prediction_batch.shape)


(32, 8)

In [0]:
# Build our new model, implementing transfer learning
colorectal_model = keras.Sequential([
  vgg_model,
  global_avg_layer,
  prediction_layer
])

In [79]:
colorectal_model.summary()


Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
vgg19 (Model)                (None, 4, 4, 512)         20024384  
_________________________________________________________________
global_average_pooling2d_2 ( (None, 512)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 8)                 4104      
=================================================================
Total params: 20,028,488
Trainable params: 4,104
Non-trainable params: 20,024,384
_________________________________________________________________

In [0]:
colorectal_model.compile(optimizer='adam',
              loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

In [81]:
colorectal_model.fit(
    train_batch,
    validation_data=val_batch,
    epochs=15
)


Epoch 1/15
125/125 [==============================] - 14s 109ms/step - loss: 1.8300 - accuracy: 0.4392 - val_loss: 1.7325 - val_accuracy: 0.5360
Epoch 2/15
125/125 [==============================] - 14s 109ms/step - loss: 1.6779 - accuracy: 0.5955 - val_loss: 1.6425 - val_accuracy: 0.6260
Epoch 3/15
125/125 [==============================] - 14s 110ms/step - loss: 1.6087 - accuracy: 0.6653 - val_loss: 1.5841 - val_accuracy: 0.6960
Epoch 4/15
125/125 [==============================] - 14s 110ms/step - loss: 1.5628 - accuracy: 0.7138 - val_loss: 1.5512 - val_accuracy: 0.7240
Epoch 5/15
125/125 [==============================] - 14s 111ms/step - loss: 1.5414 - accuracy: 0.7343 - val_loss: 1.5336 - val_accuracy: 0.7400
Epoch 6/15
125/125 [==============================] - 14s 111ms/step - loss: 1.5256 - accuracy: 0.7527 - val_loss: 1.5252 - val_accuracy: 0.7480
Epoch 7/15
125/125 [==============================] - 14s 112ms/step - loss: 1.5163 - accuracy: 0.7605 - val_loss: 1.5237 - val_accuracy: 0.7500
Epoch 8/15
125/125 [==============================] - 14s 113ms/step - loss: 1.5062 - accuracy: 0.7710 - val_loss: 1.5198 - val_accuracy: 0.7540
Epoch 9/15
125/125 [==============================] - 14s 113ms/step - loss: 1.5008 - accuracy: 0.7770 - val_loss: 1.5159 - val_accuracy: 0.7580
Epoch 10/15
125/125 [==============================] - 14s 113ms/step - loss: 1.4973 - accuracy: 0.7800 - val_loss: 1.5146 - val_accuracy: 0.7600
Epoch 11/15
125/125 [==============================] - 14s 114ms/step - loss: 1.4907 - accuracy: 0.7897 - val_loss: 1.5153 - val_accuracy: 0.7660
Epoch 12/15
125/125 [==============================] - 14s 114ms/step - loss: 1.4842 - accuracy: 0.7955 - val_loss: 1.5138 - val_accuracy: 0.7600
Epoch 13/15
125/125 [==============================] - 14s 114ms/step - loss: 1.4848 - accuracy: 0.7947 - val_loss: 1.5113 - val_accuracy: 0.7660
Epoch 14/15
125/125 [==============================] - 14s 116ms/step - loss: 1.4797 - accuracy: 0.8005 - val_loss: 1.5113 - val_accuracy: 0.7660
Epoch 15/15
125/125 [==============================] - 14s 115ms/step - loss: 1.4763 - accuracy: 0.8020 - val_loss: 1.5124 - val_accuracy: 0.7620
Out[81]:
<tensorflow.python.keras.callbacks.History at 0x7f183f413f98>

Building a text classification model with TF Hub

We'll use the IMDB movie review dataset from TF Datasets. This contains 50k movie reviews with polarized sentiment. The goal is to train a model to predict whether a review is positive or negative. We'll use TF Hub to build the first layer of our model.


In [0]:
# Get the data and split into train, test, validate
reviews_train, reviews_validate, reviews_test = tfds.load(
    'imdb_reviews', 
    split=('train[:80%]', 'train[80%:90%]', 'test'),
    as_supervised=True
)

Since this is already formatted as a tf.Data.dataset, we'll preview it by iterating over the first five examples. You should see the review and its corresponding rating. 0 is negative, 1 is positive. When we built our model, we'll use sigmoid as the output since this is a binary classification task.


In [38]:
for i in reviews_train.take(5):
  print('Review text', i[0].numpy())
  print('Review sentiment', i[1].numpy(), '\n')


Review text b"This was an absolutely terrible movie. Don't be lured in by Christopher Walken or Michael Ironside. Both are great actors, but this must simply be their worst role in history. Even their great acting could not redeem this movie's ridiculous storyline. This movie is an early nineties US propaganda piece. The most pathetic scenes were those when the Columbian rebels were making their cases for revolutions. Maria Conchita Alonso appeared phony, and her pseudo-love affair with Walken was nothing but a pathetic emotional plug in a movie that was devoid of any real meaning. I am disappointed that there are movies like this, ruining actor's like Christopher Walken's good name. I could barely sit through it."
Review sentiment 0 

Review text b'I have been known to fall asleep during films, but this is usually due to a combination of things including, really tired, being warm and comfortable on the sette and having just eaten a lot. However on this occasion I fell asleep because the film was rubbish. The plot development was constant. Constantly slow and boring. Things seemed to happen, but with no explanation of what was causing them or why. I admit, I may have missed part of the film, but i watched the majority of it and everything just seemed to happen of its own accord without any real concern for anything else. I cant recommend this film at all.'
Review sentiment 0 

Review text b'Mann photographs the Alberta Rocky Mountains in a superb fashion, and Jimmy Stewart and Walter Brennan give enjoyable performances as they always seem to do. <br /><br />But come on Hollywood - a Mountie telling the people of Dawson City, Yukon to elect themselves a marshal (yes a marshal!) and to enforce the law themselves, then gunfighters battling it out on the streets for control of the town? <br /><br />Nothing even remotely resembling that happened on the Canadian side of the border during the Klondike gold rush. Mr. Mann and company appear to have mistaken Dawson City for Deadwood, the Canadian North for the American Wild West.<br /><br />Canadian viewers be prepared for a Reefer Madness type of enjoyable howl with this ludicrous plot, or, to shake your head in disgust.'
Review sentiment 0 

Review text b'This is the kind of film for a snowy Sunday afternoon when the rest of the world can go ahead with its own business as you descend into a big arm-chair and mellow for a couple of hours. Wonderful performances from Cher and Nicolas Cage (as always) gently row the plot along. There are no rapids to cross, no dangerous waters, just a warm and witty paddle through New York life at its best. A family film in every sense and one that deserves the praise it received.'
Review sentiment 1 

Review text b'As others have mentioned, all the women that go nude in this film are mostly absolutely gorgeous. The plot very ably shows the hypocrisy of the female libido. When men are around they want to be pursued, but when no "men" are around, they become the pursuers of a 14 year old boy. And the boy becomes a man really fast (we should all be so lucky at this age!). He then gets up the courage to pursue his true love.'
Review sentiment 1 

Here we'll import a TF Hub module for text classification. Because we're using TF Hub, we can feed the data to our model directly as strings. We don't need to worry about preprocessing since TF Hub will handle converting the text to embeddings for us.


In [0]:
hub_layer = hub.KerasLayer("https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1",
                           input_shape=[], dtype=tf.string, trainable=True)

To see what the TF Hub layer is doing, let's see the embedding for an example sentence.


In [57]:
test_embedding = hub_layer(["I'm excited to try out transfer learning with TF Hub"])
print(test_embedding)


tf.Tensor(
[[ 0.3780208  -0.8602792   0.47116035  0.87798494 -1.6212037  -0.93068135
  -0.15095684  0.24388756 -0.6550514   0.12114644 -1.3243198   0.73917985
   0.52290976  0.6004198  -1.0018169   0.27700785  1.8433737   0.11805207
  -0.1830085  -0.6638955 ]], shape=(1, 20), dtype=float32)

In [58]:
model = keras.Sequential([
  hub_layer,
  keras.layers.Dense(32, activation='relu'),
  keras.layers.Dense(1, activation='sigmoid')                          
])

model.summary()


Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
keras_layer_2 (KerasLayer)   (None, 20)                400020    
_________________________________________________________________
dense_4 (Dense)              (None, 32)                672       
_________________________________________________________________
dense_5 (Dense)              (None, 1)                 33        
=================================================================
Total params: 400,725
Trainable params: 400,725
Non-trainable params: 0
_________________________________________________________________

In [0]:
model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])

In [60]:
# Note: you can train for more epochs to get higher accuracy
model.fit(
    reviews_train.shuffle(10000).batch(512), 
    validation_data=reviews_validate.batch(512), 
    epochs=15
)


Epoch 1/15
40/40 [==============================] - 4s 91ms/step - loss: 0.6748 - accuracy: 0.5717 - val_loss: 0.6563 - val_accuracy: 0.6228
Epoch 2/15
40/40 [==============================] - 3s 81ms/step - loss: 0.6539 - accuracy: 0.6571 - val_loss: 0.6410 - val_accuracy: 0.6684
Epoch 3/15
40/40 [==============================] - 4s 95ms/step - loss: 0.6380 - accuracy: 0.7063 - val_loss: 0.6244 - val_accuracy: 0.7284
Epoch 4/15
40/40 [==============================] - 4s 89ms/step - loss: 0.6209 - accuracy: 0.7558 - val_loss: 0.6104 - val_accuracy: 0.7648
Epoch 5/15
40/40 [==============================] - 4s 90ms/step - loss: 0.6063 - accuracy: 0.7919 - val_loss: 0.5997 - val_accuracy: 0.8040
Epoch 6/15
40/40 [==============================] - 4s 89ms/step - loss: 0.5946 - accuracy: 0.8185 - val_loss: 0.5903 - val_accuracy: 0.8120
Epoch 7/15
40/40 [==============================] - 4s 88ms/step - loss: 0.5841 - accuracy: 0.8413 - val_loss: 0.5834 - val_accuracy: 0.8332
Epoch 8/15
40/40 [==============================] - 4s 91ms/step - loss: 0.5761 - accuracy: 0.8586 - val_loss: 0.5793 - val_accuracy: 0.8252
Epoch 9/15
40/40 [==============================] - 4s 91ms/step - loss: 0.5702 - accuracy: 0.8672 - val_loss: 0.5747 - val_accuracy: 0.8480
Epoch 10/15
40/40 [==============================] - 4s 99ms/step - loss: 0.5641 - accuracy: 0.8832 - val_loss: 0.5714 - val_accuracy: 0.8536
Epoch 11/15
40/40 [==============================] - 4s 108ms/step - loss: 0.5594 - accuracy: 0.8938 - val_loss: 0.5688 - val_accuracy: 0.8568
Epoch 12/15
40/40 [==============================] - 4s 109ms/step - loss: 0.5553 - accuracy: 0.9011 - val_loss: 0.5671 - val_accuracy: 0.8644
Epoch 13/15
40/40 [==============================] - 4s 106ms/step - loss: 0.5518 - accuracy: 0.9093 - val_loss: 0.5657 - val_accuracy: 0.8668
Epoch 14/15
40/40 [==============================] - 4s 108ms/step - loss: 0.5485 - accuracy: 0.9166 - val_loss: 0.5639 - val_accuracy: 0.8680
Epoch 15/15
40/40 [==============================] - 4s 111ms/step - loss: 0.5456 - accuracy: 0.9212 - val_loss: 0.5644 - val_accuracy: 0.8696
Out[60]:
<tensorflow.python.keras.callbacks.History at 0x7ff2a2a77a90>

In [61]:
# Evaluate the model
results = model.evaluate(reviews_test.batch(512))


49/49 [==============================] - 4s 77ms/step - loss: 0.5705 - accuracy: 0.8641

Here we'll generate predictions on example from our test dataset, and print out the first 10 predictions along with their corresponding review text.

The output of our model is a value between 0 and 1. Values close to 0 indicate a confident negative review, and values close to 1 indicate a confident positive review.


In [0]:
prediction = model.predict(reviews_test.batch(512))

In [55]:
for i,val in enumerate(reviews_test.take(10)):
  print(val[0])
  print(prediction[i][0])
  print()


tf.Tensor(b"There are films that make careers. For George Romero, it was NIGHT OF THE LIVING DEAD; for Kevin Smith, CLERKS; for Robert Rodriguez, EL MARIACHI. Add to that list Onur Tukel's absolutely amazing DING-A-LING-LESS. Flawless film-making, and as assured and as professional as any of the aforementioned movies. I haven't laughed this hard since I saw THE FULL MONTY. (And, even then, I don't think I laughed quite this hard... So to speak.) Tukel's talent is considerable: DING-A-LING-LESS is so chock full of double entendres that one would have to sit down with a copy of this script and do a line-by-line examination of it to fully appreciate the, uh, breadth and width of it. Every shot is beautifully composed (a clear sign of a sure-handed director), and the performances all around are solid (there's none of the over-the-top scenery chewing one might've expected from a film like this). DING-A-LING-LESS is a film whose time has come.", shape=(), dtype=string)
0.43071708

tf.Tensor(b"A blackly comic tale of a down-trodden priest, Nazarin showcases the economy that Luis Bunuel was able to achieve in being able to tell a deeply humanist fable with a minimum of fuss. As an output from his Mexican era of film making, it was an invaluable talent to possess, with little money and extremely tight schedules. Nazarin, however, surpasses many of Bunuel's previous Mexican films in terms of the acting (Francisco Rabal is excellent), narrative and theme.<br /><br />The theme, interestingly, is something that was explored again in Viridiana, made three years later in Spain. It concerns the individual's struggle for humanity and altruism amongst a society that rejects any notion of virtue. Father Nazarin, however, is portrayed more sympathetically than Sister Viridiana. Whereas the latter seems to choose charity because she wishes to atone for her (perceived) sins, Nazarin's whole existence and reason for being seems to be to help others, whether they (or we) like it or not. The film's last scenes, in which he casts doubt on his behaviour and, in a split second, has to choose between the life he has been leading or the conventional life that is expected of a priest, are so emotional because they concern his moral integrity and we are never quite sure whether it remains intact or not.<br /><br />This is a remarkable film and I would urge anyone interested in classic cinema to seek it out. It is one of Bunuel's most moving films, and encapsulates many of his obsessions: frustrated desire, mad love, religious hypocrisy etc. In my view 'Nazarin' is second only to 'The Exterminating Angel', in terms of his Mexican movies, and is certainly near the top of the list of Bunuel's total filmic output.", shape=(), dtype=string)
0.521048

tf.Tensor(b'Scary Movie 1-4, Epic Movie, Date Movie, Meet the Spartans, Not another Teen Movie and Another Gay Movie. Making "Superhero Movie" the eleventh in a series that single handily ruined the parody genre. Now I\'ll admit it I have a soft spot for classics such as Airplane and The Naked Gun but you know you\'ve milked a franchise so bad when you can see the gags a mile off. In fact the only thing that might really temp you into going to see this disaster is the incredibly funny but massive sell-out Leslie Neilson.<br /><br />You can tell he needs the money, wither that or he intends to go down with the ship like a good Capitan would. In no way is he bringing down this genre but hell he\'s not helping it. But if I feel sorry for anybody in this film its decent actor Drake Bell who is put through an immense amount of embarrassment. The people who are put through the largest amount of torture by far however is the audience forced to sit through 90 minutes of laughless bile no funnier than herpes.<br /><br />After spoofing disaster films in Airplane!, police shows in The Naked Gun, and Hollywood horrors in Scary Movie 3 and 4, producer David Zucker sets his satirical sights on the superhero genre with this anarchic comedy lampooning everything from Spider-Man to X-Men and Superman Returns.<br /><br />Shortly after being bitten by a genetically altered dragonfly, high-school outcast Rick Riker (Drake Bell) begins to experience a startling transformation. Now Rick\'s skin is as strong as steel, and he possesses the strength of ten men. Determined to use his newfound powers to fight crime, Rick creates a special costume and assumes the identity of The Dragonfly -- a fearless crime fighter dedicated to keeping the streets safe for law-abiding citizens.<br /><br />But every superhero needs a nemesis, and after Lou Landers (Christopher McDonald) is caught in the middle of an experiment gone horribly awry, he develops the power to leech the life force out of anyone he meets and becomes the villainous Hourglass. Intent on achieving immortality, the Hourglass attempts to gather as much life force as possible as the noble Dragonfly sets out to take down his archenemy and realize his destiny as a true hero. Craig Mazin writes and directs this low-flying spoof.<br /><br />featuring Tracy Morgan, Pamela Anderson, Leslie Nielsen, Marion Ross, Jeffrey Tambor, and Regina Hall.<br /><br />Hell Superhero Movie may earn some merit in the fact that it\'s a hell of a lot better than Meet the Spartans and Epic Movie. But with great responsibility comes one of the worst outings of 2008 to date. Laughless but a little less irritating than Meet the Spartans. And in the same sense much more forgettable than meet the Spartans. But maybe that\'s a good reason. There are still some of us trying to scrape away the stain that was Meet the Spartans from our memory.<br /><br />My final verdict? Avoid, unless you\'re one of thoses people who enjoy such car crash cinema. As bad as Date Movie and Scary Movie 2 but not quite as bad as Meet the Spartans or Epic Movie. Super Villain.', shape=(), dtype=string)
0.8604303

tf.Tensor(b'Poor Shirley MacLaine tries hard to lend some gravitas to this mawkish, gag-inducing "feel-good" movie, but she\'s trampled by the run-away sentimentality of a film that\'s not the least bit grounded in reality.<br /><br />This was directed by Curtis Hanson? Did he have a lobotomy since we last heard from him? Hanson can do effective drama sprinkled with comedy, as evidenced by "Wonder Boys." So I don\'t know what happened to him here. This is the kind of movie that doesn\'t want to accept that life is messy and fussy, and that neat, tidy endings (however implausible they might be) might make for a nice closing shot, but come across as utterly phony if the people watching the film have been through anything remotely like what the characters in the film go through.<br /><br />My wife and I made a game of calling out the plot points before they occurred -- e.g. "the old man\'s going to teach her to read and then drop dead." Bingo! This is one of those movies where the characters give little speeches summarizing their emotional problems, making you wonder why they still have emotional problems if they\'re that aware of what\'s causing them. Toni Collette (a fine actress, by the way, and one of my favorites if not given a lot to work with here), gives a speech early on about why she buys so many shoes and never wears them, spelling out in flashing neon the film\'s awkward connecting motif. At that moment, I knew what I was in for, and the film was a downward spiral from there.<br /><br />Grade: C-', shape=(), dtype=string)
0.0047751665

tf.Tensor(b'As a former Erasmus student I enjoyed this film very much. It was so realistic and funny. It really picked up the spirit that exists among Erasmus students. I hope, many other students will follow this experience, too. However, I wonder if this movie is all that interesting to watch for people with no international experience. But at least one of my friends who has never gone on Erasmus also enjoyed it very much. I give it 9 out of 10.', shape=(), dtype=string)
0.9683135

tf.Tensor(b"My God, Ryan Gosling has made a lot of deep characters in his career, this is one of his wonderful acting jobs. For me this is a very deep movie, needs a lot of concentration, not because is difficult to watch, just because you understand it if you put your shoes in this kid, even though has everything and has famous father that is a writer, has a deeper mind, you don't understand why he kills this poor kid, until you really heard what he has to say and you start to think, at least to me, that a lot of things that he says is true. Simple kid, sweet, very gentle, in a way normal like any teenage, but inside of him suffer because he start to look at the world in a different way, then you understand why he did what he did. I recommend this movie for those who likes deep drama.", shape=(), dtype=string)
0.6410631

tf.Tensor(b"This film just won the best film award at the Cleveland International Film Festival. It's American title apparently is Autumn Spring. The acting is superb. The story takes you into the life of an elderly man who takes what life deals him and spikes it up a little bit. Abetted by his best friend (and partner in not-so-serious crime) he puts people on at every opportunity but still often reveals his heart of gold. His longsuffering wife has come to her wits end and makes a life-changing decision which is heartbreaking to watch. The resolution of the story is beautiful.", shape=(), dtype=string)
0.9731204

tf.Tensor(b'The cast for this production of Rigoletto is excellent. Edita Gruberova sings Gilda magnificently and passionately. Luciano Pavarotti also sings splendidly. Vergara is a fine Maddalena; Fedora Barbieri is a famous older singer who sings the maid, Giovanna. Weikl sings Marullo; Wixell sings both Rigoletto and Monterone. As Rigoletto, Wixell is probably the most convincing acting singer in this hard-to-beat ensemble of great singers. Kathleen Kuhlmann in the Contessa. All principals are well-known and world-renowned.<br /><br />This is an exciting Rigoletto visually as well as musically.<br /><br />I have it on both laser disc and DVD. You should have it too!', shape=(), dtype=string)
0.9980109

tf.Tensor(b'As long as you keep in mind that the production of this movie was a copyright ploy, and not intended as a serious release, it is actually surprising how not absolutely horrible it is. I even liked the theme music.<br /><br />And if ever a flick cried out for a treatment by Joel (or Mike) and the MST3K Bots, this is it! Watch this with a bunch of smart-ass wise-crackers, and you\'re in for a good time. Have a brew, butter up some large pretzels, and enjoy.<br /><br />Of course, obtaining a copy requires buying a bootleg or downloading it as shareware, but if you\'re here on the IMDb, then you\'re most likely savvy enough to do so. Good luck.<br /><br />And look for my favorite part....where Dr. Doom informs the FF that they have 12 hours to comply with his wishes....and he actually gestures the number "12" with his finger while doing so....it\'s like "Evil Sesame Street"....hoo boy.<br /><br />...and of course Mrs. Storm declaring "Just look at you....the Fanstastic Four" is just so heartwarming....you\'ll laugh, you\'ll cry.....<br /><br />So if you love schlocky Sci-Fi, this one\'s Fantastic For you!', shape=(), dtype=string)
0.03817576

tf.Tensor(b"Every great once in a while, you stumble upon a movie that exceeds even your wildest expectations. Given the IMDb rating of 4.0, I wasn't really expecting much with The Brotherhood of Satan. I hoped that at a minimum it might be cheesy fun like The Devil's Rain or any of the other early 70s similarly themed Satanic horror films. I couldn't' have been more wrong. What I got instead was an ambitious and intelligent film with a cast I really enjoyed. Speaking in broad terms to avoid giving anything away, the film's style and structure are much more experimental than the straightforward storytelling so prominent in the early 70s. The Brotherhood of Satan doesn't beat you over the head with plot points and explanations. A lot is left to the viewer to fill in the blanks. As a viewer, you know something is amiss, but for the longest period you're just not sure what it is. The unknown helps make for a far creepier atmosphere than most similar films. The ending is effective with its surreal imagery. I sat in amazement as the final credits began to roll. Those wanting a big slam-bang finale will be disappointed with the ending's simplicity. A lesser film would have tried to pull out all the stops and would, most likely, have failed miserably.<br /><br />There are moments in the film where it's easy to forget the director, Bernard McEveety, had primarily worked in television before The Brotherhood of Satan. There are a few scenes that are so well set-up, lit, and shot that even the most accomplished of directors could learn a thing or two. For example, I've seen enough films over the years to realize that directors can sometimes seem to have trouble shooting widescreen shots indoors. Not here. The scene where the men are discussing their plan of action in the sheriff's office is amazing. We see all five men at once \xc2\x96 each doing their own thing as in real life. In a lesser film, we might see all the men at once, but each would be motionless, quietly waiting their turn to deliver their dialogue. It's a small scene, but it looks so natural and is so beautifully shot that it's one of my favorite moments of The Brotherhood of Satan.<br /><br />Finally, I mentioned the acting in my opening, so without going into a long-winded speech, I'll just say that The Brotherhood of Satan features Strother Martin and L.Q. Jones. Any film with these two guys is almost an automatic winner with me.", shape=(), dtype=string)
0.4049095

Copyright 2020 Google Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License