UDP is basically just a thin wrapper around raw IP packets, it adds a port number and a checksum and thats basically it. So UDP is unreliable because the underlying IP network is unreliable. But as you might know TCP is reliable and it is also based on IP, so you can build a reliable protocol on top of an unreliable one.
The different ways to do it and their pros and cons are way to much to simply explain here (when I was in Uni, the design of TCP alone took up half of my Communication Systems course), but the very very simplified idea is that the other party sends (positive or negative) acknowledgements.
So for example with positive acknowledgements, whenever a packet arrives, the other side sends an ACK, and when you don't receive the ack for a certain amount of time, you resend that package:
[A] [B]
+----Package 1--->|
|<-----Ack 1------+
| |
+----Package 2-/ | // Package loss
| Timeout |
+----Package 2--->| // Resend after Timeout
|<-----Ack 2------+
+----Package 3--->|
...
The other way is to send negative acknowledgements when you did not receive a package:
[A] [B]
+----Package 1--->|
+----Package 2-/ | // Package loss
+----Package 3--->|
|<-----NAck 2-----+ // After receiving Package 3 B knows Package 2 is missing
+----Package 2--->| // Resend after NACK
...
Both have advantages, both have disadvantages, there are also a lot of questions with regard to how to send packages, always wait for acks, send in batches, etc. And also on how to recover from a timeout...
As I said, this stuff fills a University course.