Add "use diagnostics;" to the top of your script and run it again. You'll see a more thorough explanation, including this:
Although defined %hash is false on a plain not-yet-used hash, it becomes true in several non-obvious circumstances, including iterators, weak references, stash names, even remaining true after undef %hash. These things make defined %hash fairly useless in practice.
If a check for non-empty is what you wanted then just put it in boolean context (see "Scalar values" in perldata):
if (%hash) {
# not empty
}
There's not much I can add to that, other than to say that by following the advice above you may actually be removing an undiscovered bug.