from learntools.core import *


class Q1(CodingProblem):
    _var = 'pretrained_base'
    _hint = "`True` or `False`?"
    _correct_message = """When doing transfer learning, it's generally not a good idea to retrain the entire base -- at least not without some care. The reason is that the random weights in the head will initially create large gradient updates, which propogate back into the base layers and destroy much of the pretraining. Using techniques known as **fine tuning** it's possible to further train the base on new data, but this requires some care to do well."""
    _solution = CS('pretrained_base.trainable = False')
    def check(self, pretrained_base):
        assert ((not pretrained_base.trainable),
                ("""The base should not be trainable. Since it's already been pretrained on a large dataset, you can expect that it will be hard to improve."""))


class Q2(CodingProblem):
    hidden_units = 6
    _var = 'model'
    _hint = "You need to add two new `Dense` layers. The first should have `units={}` and `activation='relu'`. The second should have `units=1` and `activation='sigmoid'`.".format(hidden_units)
    _solution = CS(""" 
import tensorflow.keras as keras
import tensorflow.keras.layers as layers

model = Sequential([
    pretrained_base,
    layers.Flatten(),
    layers.Dense({}, activation='relu'),
    layers.Dense(1, activation='sigmoid'),
])
""".format(hidden_units))

    def check(self, model):
        hidden_units = 6
        assert ((len(model.layers) == 4),
                ("You should have four lines inside of `Sequential`. " +
                 "You had {}."
                 .format(len(model.layers))))

        layer_classes = [layer.__class__.__name__ for layer in model.layers]
        assert ((layer_classes[2] == 'Dense' and layer_classes[3] == 'Dense'),
                ("The two layers you add should both be `Dense` layers. " +
                 "You added a {} layer and a {} layer."
                 .format(layer_classes[2], layer_classes[3])))

        dense_1 = model.layers[-2]
        assert ((dense_1.units == hidden_units and
                 dense_1.activation.__name__ == 'relu'),
                ("The first dense layer should have {} units with {} activation. " +
                 "Yours had {} units and {} activation."
                 .format(hidden_units, 'relu',
                         dense_1.units, dense_1.activation.__name__)))
        
        dense_2 = model.layers[-1]
        assert ((dense_2.units == 1 and
                 dense_2.activation.__name__ == 'sigmoid'),
                ("The second dense layer should have {} units with {} activation. " +
                 "Yours had {} units and {} activation."
                 .format(hidden_units, 'sigmoid',
                         dense_2.units, dense_2.activation.__name__)))



class Q3(CodingProblem):
    _hint = "This is a *binary* classification problem."
    _solution = CS("""
model.compile(
    optimizer='adam',
    loss='binary_crossentropy',
    metrics=['binary_accuracy'],
)
""")
    _var = "model"
    def check(self, model):
        loss = model.compiled_loss._losses
        assert((loss == 'binary_crossentropy'),
               ("The loss should be `'binary_crossentropy'`. " +
                "You gave {}".format(loss)))

        metric = model.compiled_metrics._metrics
        assert((metric == 'binary_accuracy'),
               ("The metrics should be `['binary_accuracy']`. " +
                "You gave {}".format(metric)))


class Q4(ThoughtExperiment):
    _solution = """ That the training loss and validation loss stay fairly close is evidence that the model isn't just memorizing the training data, but rather learning general properties of the two classes. But, because this model converges at a loss greater than the VGG16 model, it's likely that it is underfitting some, and could benefit from some extra capacity.
"""

    
qvars = bind_exercises(
    globals(),
    [Q1, Q2, Q3, Q4],
    var_format='q_{n}',
)

__all__ = list(qvars)

@@ -683,17 +683,16 @@
  assert 
-(
 (not pre
@@ -715,18 +715,19 @@
 inable),
-%0A
  
+%5C%0A
         
@@ -877,17 +877,16 @@
 ove.%22%22%22)
-)
 %0A%0A%0Aclass
@@ -1484,17 +1484,16 @@
  assert 
-(
 (len(mod
@@ -1509,18 +1509,19 @@
 ) == 4),
-%0A
  
+%5C%0A
         
@@ -1659,17 +1659,16 @@
 ayers)))
-)
 %0A%0A      
@@ -1753,17 +1753,16 @@
  assert 
-(
 (layer_c
@@ -1815,18 +1815,19 @@
 Dense'),
-%0A
  
+%5C%0A
         
@@ -2010,17 +2010,16 @@
 ses%5B3%5D))
-)
 %0A%0A      
@@ -2058,25 +2058,24 @@
      assert 
-(
 (dense_1.uni
@@ -2093,25 +2093,24 @@
 n_units and%0A
- 
             
@@ -2148,24 +2148,26 @@
  == 'relu'),
+ %5C
 %0A           
@@ -2413,17 +2413,16 @@
 name__))
-)
 %0A       
@@ -2473,17 +2473,16 @@
  assert 
-(
 (dense_2
@@ -2493,25 +2493,24 @@
 ts == 1 and%0A
- 
             
@@ -2555,16 +2555,18 @@
 gmoid'),
+ %5C
 %0A       
@@ -2820,17 +2820,16 @@
 name__))
-)
 %0A%0A%0A%0Aclas
@@ -3057,16 +3057,17 @@
 %22model%22%0A
+%0A
     def 
@@ -3143,17 +3143,17 @@
   assert
-(
+ 
 (loss ==
@@ -3172,28 +3172,27 @@
 ssentropy'),
-%0A   
+ %5C%0A
             
@@ -3246,35 +3246,32 @@
  +%0A             
-   
 %22You gave %7B%7D%22.fo
@@ -3281,17 +3281,16 @@
 t(loss))
-)
 %0A%0A      
@@ -3346,17 +3346,17 @@
   assert
-(
+ 
 (metric 
@@ -3377,20 +3377,19 @@
 uracy'),
-%0A   
+ %5C%0A
         
@@ -3448,35 +3448,32 @@
  +%0A             
-   
 %22You gave %7B%7D%22.fo
@@ -3485,17 +3485,16 @@
 metric))
-)
 %0A%0A%0Aclass

Fix exercise 1 checking