Week 3 programming assignment: cost computation test case fails because A2 has a wrong shape

Now it’s getting nitty-gritty. :grimacing:

In week 3’s programming assignment, “planar data classification with one hidden layer”, we are progressing nicely, but then in the “GRADED FUNCTION: compute_cost”, the test case fails.

And it fails apparently because “The shape of A2 is (5,)”!

More precisely. I am getting three calls which are nice and one which is nasty:

The shape of A2 is (1, 3)
The shape of Y is (1, 3)

No problem!

The shape of A2 is (1, 3)
The shape of Y is (1, 3)

No problem!

The shape of A2 is (1, 5)
The shape of Y is (1, 5)

No problem!

The shape of A2 is (5,)
The shape of Y is (1, 5)

Well, this is just garbage input.

Now the question is

  • Am I at fault somehow due to bad implementation of earlier code, or
  • Is the test case faulty?

Apparently nobody has complained so far, so it must be me, right?

Also, the Y vector, which was created by load_planar_dataset() as a matrix of shape (1,400) containing uint8 from {0,1} now contains booleans. :thinking: I can handle that :muscle: but I was a bit surprised about that nevertheless. Am I supposed to be expecting this?

Btw, I am using print statements to STDERR with good effect, can I leave these in? Will the grader croak on those or does it just scrutinize STDOUT (that would be convenient)?

Best regards,

– David

In general it should not matter if you leave some prints in your code. The grader calls your functions and checks the return values. But if the text output is really extreme (in the inner loop that gets executed 10k times), then the volume of the output may well slow things down or cause problems. To be on the safe side, you can clear the output:

Kernel -> Restart and Clear Output
Save
Submit to grader

The point being what I said above: the grader does not care about the text output.

If you fail the tests, then the first thing to assume is that your code is wrong. I did not encounter any problems in that section and literally thousands of students have taken this course since it was first published. But, that having been said, they did recently publish a bug fix to this assignment. Nothing ever stays completely static.

What is different about the inputs in the failing case? That should be a clue. If the test is not in the notebook, you can find it in public_tests.py.

1 Like

Interesting. I added the same prints to my notebook and you’re right that the test case is different. But that did not cause any problem in my code. You have basically two choices here: you can use np.dot to implement the core of this computation or you can use elementwise multiply followed by np.sum. I used np.dot. Generally when you dot 2D vectors with 1D vectors, the results can be funky. But in this case the 2D vector has one trivial dimension and things seem to work.

1 Like

I don’t know what’s going wrong exactly, it’s just that the test cases provide bad data.

I have now managed to get:

All tests passed!

on that exercise, but only by using argument cleaning:

Bludgeon A2 into proper shape

    print(f"The shape of A2 is {A2.shape} with {A2.ndim} axes", file=sys.stderr)
    print(f"The shape of Y is {Y.shape} with {Y.ndim} axes", file=sys.stderr)
    print(f"The content of Y is {Y}", file=sys.stderr)
    
    if (A2.ndim == 1):
        print(f"Suspect A2 with shape {A2.shape} - reshaping!", file=sys.stderr)
        A2 = A2.reshape(1, A2.shape[0])
        print(f"The shape of A2 is now {A2.shape} with {A2.ndim} axes", file=sys.stderr)

Bludgeon Y’s content into integers

    if np.issubdtype(Y.dtype, np.bool_):
        print("Y contains booleans - converting...", file=sys.stderr)
        Y = Y.astype(np.uint8)
        print(f"The content of Y is now {Y}", file=sys.stderr)
    elif np.issubdtype(Y.dtype, np.integer):
        print("Y contains integers - good...", file=sys.stderr)
        pass
    else:
        raise "Y contains neither exclusively booleans nor exclusively integers"
    
    # A condition built using "elementwise |"
    
    assert np.all((Y == 0) | (Y == 1)), f"Array contains values outside of {0,1}"

It could well be that Numpy transparently works on even suspect input, and everyone has lived with this heretofore. I have added assertions to check everything is nice and tidy, so these are triggerred …

Sometimes I ask myself why I still prefer statically typed, compile-time-checked languages (with assertions added, too). And then I remember. :sweat:

P.S.

I noticed the problem with the booleans in the Y because I’m trying to mask the elements of A2 that just drop out in function of y being 0 or 1. No need to compute logarithms if you don’t have to.

Ok, you’re right that this is a bug in the test case. Here’s the code from public_tests.py:

def compute_cost_test(target):
    np.random.seed(1)
    Y_2 = (np.random.randn(1, 5) > 0)
    A2 = (np.array([[ 0.5002307 ,  0.49985831,  0.50023963, 0.25, 0.7]]))
    
    A3 = np.array([0.7,0.3,0.8, 0.9,0.9])
    a3_Y = np.array([1,0,1,1,1]).reshape(1,5)

    expected_output_1 = 0.5447066599017815
    output_1 = target(A2, Y_2)
    
    expected_output_2 = 0.22944289410146546
    output_2 = target(A3, a3_Y)

You can see that there is only one set of brackets on the A3 value, which is what causes it to be a 1D vector.

Now the question is how serious a bug is that. What did you do in your code such that this resulted in a failure? I tried both the np.dot solution and the * followed by np.sum solution and both worked just fine for me with that input data. So what did you do that caused the failure?

1 Like

Please just show us the exception trace.

I noticed the problem with the booleans in the Y because I’m trying to mask the elements of A2 that just drop out in function of y being 0 or 1. (No need to compute logarithms if you don’t have to.)

For the rest, I have added a lot of asserts to test my assumptions, this might cause it.

Hold on a second… I will try to find it.

Okay, it’s simply because I retrieved m for no particular reason.

m       = A2.shape[1]

Of course this yields:

IndexError: tuple index out of range

A second failure occurs if I try to mask A2 by Y == 0 using numpy.ma, then core numpy complains:

IndexError: Inconsistent shape between the condition and the input (got (1, 5) and (5,))

I don’t understand your point: there should be no case in which you can skip computing the logs of the elements of A2 or (1 - A2). They should never be exactly 0 or 1, although it can happen because of floating point “saturation”. That won’t happen in any of our test cases here. Or if it does, then there’s something wrong with your code.

There is also no need to coerce the Y label values to be integers. Booleans (if they happen to be such) will automatically be coerced to integers or floats by numpy as required.

Yes you can - because you decide whether to compute the log based on the true label, not the prediction. And the true label is always exactly 0 or 1:

Indeed so. But I need to decide – as the masking condition will either expect an integer or a boolean.

Ok, I see now what you are trying to do. You are working way way too hard and are probably shooting yourself in the foot, if your intent is to make the code as efficient as possible. Everything here in 2025 is best when vectorized. That’s where the speed comes from. You’re adding a bunch of additional logic that adds complexity and may result in the operations not being vectorized. The 1980’s were a looong time ago and a lot has changed. How many generations of Moore’s Law ago is that? What was the maximum size of a CPU chip in terms of transistors in those days? I don’t even remember. You’ve probably got more transistors in your pocket right now than existed in the whole state of California in 1980.

If you used to write assembly language code in those days to get every last cycle out of your hardware, you’d be better off setting that mindset aside. Just go through the courses and “lean into” python. Sure you can do better, but let’s learn the lay of the land first and then if you really want to “go deep”, you can do that later by writing TF or PyTorch internals in c.

No, it’s still vectorized. It’s 5 lines of code.

But it fails where both of my versions don’t. And both of mine are 1 line of code. So why is that better?

Of course ‘lines of code’ is basically a bogus metric. My 1 lines both have a fair number of operations in them.

Well, we have got to measure later, but note that the problem is not in your or my code - it is in the test case, which provides bad data. Data that needs silent conversion is less than ideal. Who knows what can happen.

Fair enough. I will file a bug about the test case. But now let’s move on …

1 Like