Skip to content

Commit aa19f69

Browse files
committed
Ensure the actual dropout rate == requested dropout rate in most cases
1 parent 53b9663 commit aa19f69

File tree

3 files changed

+63
-9
lines changed

3 files changed

+63
-9
lines changed

src/nf/nf_dropout_layer_submodule.f90

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
submodule (nf_dropout_layer) nf_dropout_layer_submodule
2+
use nf_random, only: shuffle
23
!! This submodule implements the procedures defined in the
34
!! nf_dropout_layer module.
45

@@ -37,12 +38,13 @@ module subroutine forward(self, input)
3738
! Generate random mask for dropout, training mode only
3839
if (self % training) then
3940

40-
call random_number(self % mask)
41-
where (self % mask < self % dropout_rate)
42-
self % mask = 0
43-
elsewhere
44-
self % mask = 1
45-
end where
41+
! Set the first dropout_rate number of elements to 0, the rest to 1,
42+
! and shuffle. Note that the selection of the elements rounds down to
43+
! the nearest integer, so in cases where size(input) * dropout_rate is
44+
! not an integer, the actual dropout rate will be slightly lower.
45+
self % mask = 1
46+
self % mask(:int(size(self % mask) * self % dropout_rate)) = 0
47+
call shuffle(self % mask)
4648

4749
! Scale factor to preserve the input sum
4850
self % scale = sum(input) / sum(input * self % mask)

src/nf/nf_random.f90

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
module nf_random
22

3-
!! Provides a random number generator with
4-
!! normal distribution, centered on zero.
3+
!! Provides a random number generator with normal distribution,
4+
!! centered on zero, and a Fisher-Yates shuffle.
55

66
implicit none
77

88
private
9-
public :: random_normal
9+
public :: random_normal, shuffle
1010

1111
real, parameter :: pi = 4 * atan(1.d0)
1212

@@ -23,4 +23,22 @@ impure elemental subroutine random_normal(x)
2323
x = sqrt(- 2 * log(u(1))) * cos(2 * pi * u(2))
2424
end subroutine random_normal
2525

26+
27+
subroutine shuffle(x)
28+
!! Fisher-Yates shuffle.
29+
real, intent(in out) :: x(:)
30+
!! Array to shuffle
31+
integer :: i, j
32+
real :: r, temp
33+
34+
do i = size(x), 2, -1
35+
call random_number(r)
36+
j = floor(r * i) + 1
37+
temp = x(i)
38+
x(i) = x(j)
39+
x(j) = temp
40+
end do
41+
42+
end subroutine shuffle
43+
2644
end module nf_random

test/test_dropout_layer.f90

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,40 @@ program test_dropout_layer
7575

7676
end select
7777

78+
! Test that the generated dropout mask matches the requested dropout rate.
79+
test_mask: block
80+
integer, parameter :: input_sizes(3) = [10, 100, 1000]
81+
real, parameter :: dropout_rates(5) = [0., 0.2, 0.5, 0.8, 1.]
82+
real, allocatable :: input_data(:)
83+
integer :: i, j
84+
85+
do i = 1, size(input_sizes)
86+
do j = 1, size(dropout_rates)
87+
88+
net = network([ &
89+
input(input_sizes(i)), &
90+
dropout(dropout_rates(j)) &
91+
])
92+
93+
if (allocated(input_data)) deallocate(input_data)
94+
allocate(input_data(input_sizes(i)))
95+
call random_number(input_data)
96+
97+
call net % forward(input_data)
98+
99+
select type(layer1_p => net % layers(2) % p)
100+
type is(dropout_layer)
101+
if (abs(sum(layer1_p % mask) / size(layer1_p % mask) - (1 - dropout_rates(j))) > 1e-6) then
102+
ok = .false.
103+
write(stderr, '(a)') 'actual dropout rate is equal to requested.. failed'
104+
end if
105+
end select
106+
end do
107+
end do
108+
109+
end block test_mask
110+
111+
78112
! Now we're gonna run the forward pass and check that the dropout indeed
79113
! drops according to the requested dropout rate.
80114
forward_pass: block

0 commit comments

Comments
 (0)